mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 11:50:14 +01:00
merge: merge branch systemd into master
This commit is contained in:
commit
d43a0459bc
18 changed files with 564 additions and 480 deletions
|
|
@ -4308,9 +4308,9 @@ ipv4ll_start (NMDevice *self, NMDeviceStateReason *reason)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_ipv4ll_set_index (priv->ipv4ll, ifindex);
|
||||
r = sd_ipv4ll_set_ifindex (priv->ipv4ll, ifindex);
|
||||
if (r < 0) {
|
||||
_LOGE (LOGD_AUTOIP4, "IPv4LL: set_index() failed with error %d", r);
|
||||
_LOGE (LOGD_AUTOIP4, "IPv4LL: set_ifindex() failed with error %d", r);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -604,9 +604,9 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
|
|||
}
|
||||
}
|
||||
|
||||
r = sd_dhcp_client_set_index (priv->client4, nm_dhcp_client_get_ifindex (client));
|
||||
r = sd_dhcp_client_set_ifindex (priv->client4, nm_dhcp_client_get_ifindex (client));
|
||||
if (r < 0) {
|
||||
_LOGW ("failed to set ifindex (%d)", r);
|
||||
_LOGW ("failed to set ififindex (%d)", r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
@ -932,7 +932,7 @@ ip6_start (NMDhcpClient *client,
|
|||
}
|
||||
}
|
||||
|
||||
r = sd_dhcp6_client_set_index (priv->client6, nm_dhcp_client_get_ifindex (client));
|
||||
r = sd_dhcp6_client_set_ifindex (priv->client6, nm_dhcp_client_get_ifindex (client));
|
||||
if (r < 0) {
|
||||
_LOGW ("failed to set ifindex (%d)", r);
|
||||
goto error;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "ether-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
|
||||
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
|
||||
assert(addr);
|
||||
|
|
@ -56,3 +57,71 @@ bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
|
|||
a->ether_addr_octet[4] == b->ether_addr_octet[4] &&
|
||||
a->ether_addr_octet[5] == b->ether_addr_octet[5];
|
||||
}
|
||||
|
||||
int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) {
|
||||
size_t pos = 0, n, field;
|
||||
char sep = '\0';
|
||||
const char *hex = HEXDIGITS, *hexoff;
|
||||
size_t x;
|
||||
bool touched;
|
||||
|
||||
#define parse_fields(v) \
|
||||
for (field = 0; field < ELEMENTSOF(v); field++) { \
|
||||
touched = false; \
|
||||
for (n = 0; n < (2 * sizeof(v[0])); n++) { \
|
||||
if (s[pos] == '\0') \
|
||||
break; \
|
||||
hexoff = strchr(hex, s[pos]); \
|
||||
if (hexoff == NULL) \
|
||||
break; \
|
||||
assert(hexoff >= hex); \
|
||||
x = hexoff - hex; \
|
||||
if (x >= 16) \
|
||||
x -= 6; /* A-F */ \
|
||||
assert(x < 16); \
|
||||
touched = true; \
|
||||
v[field] <<= 4; \
|
||||
v[field] += x; \
|
||||
pos++; \
|
||||
} \
|
||||
if (!touched) \
|
||||
return -EINVAL; \
|
||||
if (field < (ELEMENTSOF(v)-1)) { \
|
||||
if (s[pos] != sep) \
|
||||
return -EINVAL; \
|
||||
else \
|
||||
pos++; \
|
||||
} \
|
||||
}
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
|
||||
sep = s[strspn(s, hex)];
|
||||
if (sep == '\n')
|
||||
return -EINVAL;
|
||||
if (strchr(":.-", sep) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (sep == '.') {
|
||||
uint16_t shorts[3] = { 0 };
|
||||
|
||||
parse_fields(shorts);
|
||||
|
||||
for (n = 0; n < ELEMENTSOF(shorts); n++) {
|
||||
ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8);
|
||||
ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff);
|
||||
}
|
||||
} else {
|
||||
struct ether_addr out = { .ether_addr_octet = { 0 } };
|
||||
|
||||
parse_fields(out.ether_addr_octet);
|
||||
|
||||
for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++)
|
||||
ret->ether_addr_octet[n] = out.ether_addr_octet[n];
|
||||
}
|
||||
|
||||
if (offset)
|
||||
*offset = pos;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,3 +35,5 @@ bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b);
|
|||
static inline bool ether_addr_is_null(const struct ether_addr *addr) {
|
||||
return ether_addr_equal(addr, ÐER_ADDR_NULL);
|
||||
}
|
||||
|
||||
int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
|
||||
#define ALPHANUMERICAL LETTERS DIGITS
|
||||
#define HEXDIGITS DIGITS "abcdefABCDEF"
|
||||
|
||||
#define streq(a,b) (strcmp((a),(b)) == 0)
|
||||
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
|
||||
|
|
|
|||
|
|
@ -65,4 +65,5 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
|
|||
#define DHCP_CLIENT_DONT_DESTROY(client) \
|
||||
_cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
|
||||
|
||||
#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
|
||||
#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
|
||||
#define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ typedef struct DHCPPacket DHCPPacket;
|
|||
#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
|
||||
#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage))
|
||||
#define DHCP_DEFAULT_MIN_SIZE 576 /* the minimum internet hosts must be able to receive */
|
||||
#define DHCP_MIN_OPTIONS_SIZE DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE
|
||||
#define DHCP_MIN_OPTIONS_SIZE (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE)
|
||||
#define DHCP_MAGIC_COOKIE (uint32_t)(0x63825363)
|
||||
|
||||
enum {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ struct DHCP6IA {
|
|||
|
||||
typedef struct DHCP6IA DHCP6IA;
|
||||
|
||||
#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
|
||||
#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
|
||||
#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
||||
size_t optlen, const void *optval);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "conf-parser.h"
|
||||
#endif /* NM_IGNORED */
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "log.h"
|
||||
#include "network-internal.h"
|
||||
|
|
@ -277,6 +278,8 @@ int config_parse_hwaddr(const char *unit,
|
|||
void *userdata) {
|
||||
struct ether_addr **hwaddr = data;
|
||||
struct ether_addr *n;
|
||||
const char *start;
|
||||
size_t offset;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
|
|
@ -288,14 +291,10 @@ int config_parse_hwaddr(const char *unit,
|
|||
if (!n)
|
||||
return log_oom();
|
||||
|
||||
r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
|
||||
&n->ether_addr_octet[0],
|
||||
&n->ether_addr_octet[1],
|
||||
&n->ether_addr_octet[2],
|
||||
&n->ether_addr_octet[3],
|
||||
&n->ether_addr_octet[4],
|
||||
&n->ether_addr_octet[5]);
|
||||
if (r != 6) {
|
||||
start = rvalue + strspn(rvalue, WHITESPACE);
|
||||
r = ether_addr_from_string(start, n, &offset);
|
||||
|
||||
if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
|
||||
free(n);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ struct sd_dhcp_client {
|
|||
sd_event *event;
|
||||
int event_priority;
|
||||
sd_event_source *timeout_resend;
|
||||
int index;
|
||||
int ifindex;
|
||||
int fd;
|
||||
union sockaddr_union link;
|
||||
sd_event_source *receive_message;
|
||||
|
|
@ -103,7 +103,7 @@ struct sd_dhcp_client {
|
|||
sd_event_source *timeout_t1;
|
||||
sd_event_source *timeout_t2;
|
||||
sd_event_source *timeout_expire;
|
||||
sd_dhcp_client_callback_t cb;
|
||||
sd_dhcp_client_callback_t callback;
|
||||
void *userdata;
|
||||
sd_dhcp_lease *lease;
|
||||
usec_t start_delay;
|
||||
|
|
@ -133,9 +133,10 @@ int sd_dhcp_client_set_callback(
|
|||
sd_dhcp_client *client,
|
||||
sd_dhcp_client_callback_t cb,
|
||||
void *userdata) {
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->cb = cb;
|
||||
client->callback = cb;
|
||||
client->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
|
|
@ -153,10 +154,10 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
|
|||
size_t i;
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return (IN_SET(client->state, DHCP_STATE_INIT,
|
||||
DHCP_STATE_STOPPED), -EBUSY);
|
||||
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
|
||||
|
||||
switch(option) {
|
||||
|
||||
case SD_DHCP_OPTION_PAD:
|
||||
case SD_DHCP_OPTION_OVERLOAD:
|
||||
case SD_DHCP_OPTION_MESSAGE_TYPE:
|
||||
|
|
@ -184,9 +185,9 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
|
|||
int sd_dhcp_client_set_request_address(
|
||||
sd_dhcp_client *client,
|
||||
const struct in_addr *last_addr) {
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return (IN_SET(client->state, DHCP_STATE_INIT,
|
||||
DHCP_STATE_STOPPED), -EBUSY);
|
||||
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
|
||||
|
||||
if (last_addr)
|
||||
client->last_addr = last_addr->s_addr;
|
||||
|
|
@ -196,14 +197,13 @@ int sd_dhcp_client_set_request_address(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
|
||||
int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return (IN_SET(client->state, DHCP_STATE_INIT,
|
||||
DHCP_STATE_STOPPED), -EBUSY);
|
||||
assert_return(interface_index > 0, -EINVAL);
|
||||
|
||||
client->index = interface_index;
|
||||
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
|
||||
client->ifindex = ifindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -233,8 +233,7 @@ int sd_dhcp_client_set_mac(
|
|||
return 0;
|
||||
|
||||
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
|
||||
log_dhcp_client(client, "Changing MAC address on running DHCP "
|
||||
"client, restarting");
|
||||
log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
|
||||
need_restart = true;
|
||||
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
||||
}
|
||||
|
|
@ -286,14 +285,17 @@ int sd_dhcp_client_set_client_id(
|
|||
assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
|
||||
|
||||
switch (type) {
|
||||
|
||||
case ARPHRD_ETHER:
|
||||
if (data_len != ETH_ALEN)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case ARPHRD_INFINIBAND:
|
||||
if (data_len != INFINIBAND_ALEN)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -351,7 +353,7 @@ int sd_dhcp_client_set_iaid_duid(
|
|||
|
||||
/* If IAID is not configured, generate it. */
|
||||
if (iaid == 0) {
|
||||
r = dhcp_identifier_set_iaid(client->index, client->mac_addr,
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
|
||||
client->mac_addr_len,
|
||||
&client->client_id.ns.iaid);
|
||||
if (r < 0)
|
||||
|
|
@ -439,28 +441,29 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
|
|||
|
||||
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (client->state != DHCP_STATE_BOUND &&
|
||||
client->state != DHCP_STATE_RENEWING &&
|
||||
client->state != DHCP_STATE_REBINDING)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
*ret = client->lease;
|
||||
if (ret)
|
||||
*ret = client->lease;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void client_notify(sd_dhcp_client *client, int event) {
|
||||
if (client->cb)
|
||||
client->cb(client, event, client->userdata);
|
||||
assert(client);
|
||||
|
||||
if (client->callback)
|
||||
client->callback(client, event, client->userdata);
|
||||
}
|
||||
|
||||
static int client_initialize(sd_dhcp_client *client) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->receive_message =
|
||||
sd_event_source_unref(client->receive_message);
|
||||
client->receive_message = sd_event_source_unref(client->receive_message);
|
||||
|
||||
client->fd = asynchronous_close(client->fd);
|
||||
|
||||
|
|
@ -569,7 +572,7 @@ static int client_message_init(
|
|||
|
||||
client->client_id.type = 255;
|
||||
|
||||
r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -755,8 +758,9 @@ static int client_send_request(sd_dhcp_client *client) {
|
|||
size_t optoffset, optlen;
|
||||
int r;
|
||||
|
||||
r = client_message_init(client, &request, DHCP_REQUEST,
|
||||
&optlen, &optoffset);
|
||||
assert(client);
|
||||
|
||||
r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -853,18 +857,23 @@ static int client_send_request(sd_dhcp_client *client) {
|
|||
return r;
|
||||
|
||||
switch (client->state) {
|
||||
|
||||
case DHCP_STATE_REQUESTING:
|
||||
log_dhcp_client(client, "REQUEST (requesting)");
|
||||
break;
|
||||
|
||||
case DHCP_STATE_INIT_REBOOT:
|
||||
log_dhcp_client(client, "REQUEST (init-reboot)");
|
||||
break;
|
||||
|
||||
case DHCP_STATE_RENEWING:
|
||||
log_dhcp_client(client, "REQUEST (renewing)");
|
||||
break;
|
||||
|
||||
case DHCP_STATE_REBINDING:
|
||||
log_dhcp_client(client, "REQUEST (rebinding)");
|
||||
break;
|
||||
|
||||
default:
|
||||
log_dhcp_client(client, "REQUEST (invalid)");
|
||||
break;
|
||||
|
|
@ -896,6 +905,7 @@ static int client_timeout_resend(
|
|||
goto error;
|
||||
|
||||
switch (client->state) {
|
||||
|
||||
case DHCP_STATE_RENEWING:
|
||||
|
||||
time_left = (client->lease->t2 - client->lease->t1) / 2;
|
||||
|
|
@ -1105,15 +1115,14 @@ static int client_start_delayed(sd_dhcp_client *client) {
|
|||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(client->event, -EINVAL);
|
||||
assert_return(client->index > 0, -EINVAL);
|
||||
assert_return(client->ifindex > 0, -EINVAL);
|
||||
assert_return(client->fd < 0, -EBUSY);
|
||||
assert_return(client->xid == 0, -EINVAL);
|
||||
assert_return(client->state == DHCP_STATE_INIT ||
|
||||
client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
|
||||
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY);
|
||||
|
||||
client->xid = random_u32();
|
||||
|
||||
r = dhcp_network_bind_raw_socket(client->index, &client->link,
|
||||
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
|
||||
client->xid, client->mac_addr,
|
||||
client->mac_addr_len, client->arp_type);
|
||||
if (r < 0) {
|
||||
|
|
@ -1155,13 +1164,15 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
|
|||
DHCP_CLIENT_DONT_DESTROY(client);
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
|
||||
client->receive_message = sd_event_source_unref(client->receive_message);
|
||||
client->fd = asynchronous_close(client->fd);
|
||||
|
||||
client->state = DHCP_STATE_REBINDING;
|
||||
client->attempt = 1;
|
||||
|
||||
r = dhcp_network_bind_raw_socket(client->index, &client->link,
|
||||
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
|
||||
client->xid, client->mac_addr,
|
||||
client->mac_addr_len, client->arp_type);
|
||||
if (r < 0) {
|
||||
|
|
@ -1628,7 +1639,7 @@ static int client_receive_message_udp(
|
|||
|
||||
sd_dhcp_client *client = userdata;
|
||||
_cleanup_free_ DHCPMessage *message = NULL;
|
||||
const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
|
||||
const struct ether_addr zero_mac = {};
|
||||
const struct ether_addr *expected_chaddr = NULL;
|
||||
uint8_t expected_hlen = 0;
|
||||
ssize_t len, buflen;
|
||||
|
|
@ -1644,14 +1655,14 @@ static int client_receive_message_udp(
|
|||
if (!message)
|
||||
return -ENOMEM;
|
||||
|
||||
len = read(fd, message, buflen);
|
||||
len = recv(fd, message, buflen, 0);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
|
||||
log_dhcp_client(client, "Could not receive message from UDP socket: %m");
|
||||
return -errno;
|
||||
} else if ((size_t)len < sizeof(DHCPMessage)) {
|
||||
return log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket: %m");
|
||||
}
|
||||
if ((size_t) len < sizeof(DHCPMessage)) {
|
||||
log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1782,7 +1793,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
|
|||
|
||||
r = client_start(client);
|
||||
if (r >= 0)
|
||||
log_dhcp_client(client, "STARTED on ifindex %i", client->index);
|
||||
log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
@ -1826,8 +1837,7 @@ int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
|
|||
}
|
||||
|
||||
sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
|
||||
if (!client)
|
||||
return NULL;
|
||||
assert_return(client, NULL);
|
||||
|
||||
return client->event;
|
||||
}
|
||||
|
|
@ -1883,13 +1893,12 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
|
|||
|
||||
client->n_ref = 1;
|
||||
client->state = DHCP_STATE_INIT;
|
||||
client->index = -1;
|
||||
client->ifindex = -1;
|
||||
client->fd = -1;
|
||||
client->attempt = 1;
|
||||
client->mtu = DHCP_DEFAULT_MIN_SIZE;
|
||||
|
||||
client->req_opts_size = ELEMENTSOF(default_req_opts);
|
||||
|
||||
client->req_opts = memdup(default_req_opts, client->req_opts_size);
|
||||
if (!client->req_opts)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ struct sd_dhcp6_client {
|
|||
enum DHCP6State state;
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
int index;
|
||||
int ifindex;
|
||||
struct in6_addr local_address;
|
||||
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
|
||||
size_t mac_addr_len;
|
||||
|
|
@ -66,7 +66,7 @@ struct sd_dhcp6_client {
|
|||
uint8_t retransmit_count;
|
||||
sd_event_source *timeout_resend;
|
||||
sd_event_source *timeout_resend_expire;
|
||||
sd_dhcp6_client_callback_t cb;
|
||||
sd_dhcp6_client_callback_t callback;
|
||||
void *userdata;
|
||||
struct duid duid;
|
||||
size_t duid_len;
|
||||
|
|
@ -117,22 +117,22 @@ int sd_dhcp6_client_set_callback(
|
|||
sd_dhcp6_client *client,
|
||||
sd_dhcp6_client_callback_t cb,
|
||||
void *userdata) {
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->cb = cb;
|
||||
client->callback = cb;
|
||||
client->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(interface_index >= -1, -EINVAL);
|
||||
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(ifindex >= -1, -EINVAL);
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
|
||||
client->index = interface_index;
|
||||
|
||||
client->ifindex = ifindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -260,6 +260,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
|
|||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
switch(option) {
|
||||
|
||||
case SD_DHCP6_OPTION_DNS_SERVERS:
|
||||
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
||||
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
||||
|
|
@ -296,20 +297,25 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
|
|||
}
|
||||
|
||||
static void client_notify(sd_dhcp6_client *client, int event) {
|
||||
if (client->cb)
|
||||
client->cb(client, event, client->userdata);
|
||||
assert(client);
|
||||
|
||||
if (client->callback)
|
||||
client->callback(client, event, client->userdata);
|
||||
}
|
||||
|
||||
static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
|
||||
assert(client);
|
||||
|
||||
if (client->lease) {
|
||||
dhcp6_lease_clear_timers(&client->lease->ia);
|
||||
sd_dhcp6_lease_unref(client->lease);
|
||||
}
|
||||
|
||||
client->lease = lease;
|
||||
}
|
||||
|
||||
static int client_reset(sd_dhcp6_client *client) {
|
||||
assert_return(client, -EINVAL);
|
||||
assert(client);
|
||||
|
||||
client_set_lease(client, NULL);
|
||||
|
||||
|
|
@ -357,6 +363,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
usec_t elapsed_usec;
|
||||
be16_t elapsed_time;
|
||||
|
||||
assert(client);
|
||||
|
||||
len = sizeof(DHCP6Message) + optlen;
|
||||
|
||||
message = malloc0(len);
|
||||
|
|
@ -458,9 +466,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_dhcp6_client *client = userdata;
|
||||
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(client->lease, -EINVAL);
|
||||
assert(s);
|
||||
assert(client);
|
||||
assert(client->lease);
|
||||
|
||||
client->lease->ia.timeout_t2 =
|
||||
sd_event_source_unref(client->lease->ia.timeout_t2);
|
||||
|
|
@ -475,9 +483,9 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
|
|||
static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_dhcp6_client *client = userdata;
|
||||
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(client->lease, -EINVAL);
|
||||
assert(s);
|
||||
assert(client);
|
||||
assert(client->lease);
|
||||
|
||||
client->lease->ia.timeout_t1 =
|
||||
sd_event_source_unref(client->lease->ia.timeout_t1);
|
||||
|
|
@ -675,7 +683,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
|
|||
if (client->ia_na.id)
|
||||
return 0;
|
||||
|
||||
r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -694,6 +702,11 @@ static int client_parse_message(
|
|||
bool clientid = false;
|
||||
be32_t iaid_lease;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(len >= sizeof(DHCP6Message));
|
||||
assert(lease);
|
||||
|
||||
option = (uint8_t *)message + sizeof(DHCP6Message);
|
||||
len -= sizeof(DHCP6Message);
|
||||
|
||||
|
|
@ -838,9 +851,12 @@ static int client_parse_message(
|
|||
}
|
||||
|
||||
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
|
||||
int r;
|
||||
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
||||
bool rapid_commit;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(reply);
|
||||
|
||||
if (reply->type != DHCP6_REPLY)
|
||||
return 0;
|
||||
|
|
@ -869,9 +885,9 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
|
|||
}
|
||||
|
||||
static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
|
||||
int r;
|
||||
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
||||
uint8_t pref_advertise = 0, pref_lease = 0;
|
||||
int r;
|
||||
|
||||
if (advertise->type != DHCP6_ADVERTISE)
|
||||
return 0;
|
||||
|
|
@ -902,7 +918,12 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver
|
|||
return r;
|
||||
}
|
||||
|
||||
static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
static int client_receive_message(
|
||||
sd_event_source *s,
|
||||
int fd, uint32_t
|
||||
revents,
|
||||
void *userdata) {
|
||||
|
||||
sd_dhcp6_client *client = userdata;
|
||||
DHCP6_CLIENT_DONT_DESTROY(client);
|
||||
_cleanup_free_ DHCP6Message *message = NULL;
|
||||
|
|
@ -921,16 +942,18 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
|
|||
if (!message)
|
||||
return -ENOMEM;
|
||||
|
||||
len = read(fd, message, buflen);
|
||||
len = recv(fd, message, buflen, 0);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
|
||||
log_dhcp6_client(client, "Could not receive message from UDP socket: %m");
|
||||
return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
|
||||
|
||||
return -errno;
|
||||
} else if ((size_t)len < sizeof(DHCP6Message))
|
||||
}
|
||||
if ((size_t) len < sizeof(DHCP6Message)) {
|
||||
log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(message->type) {
|
||||
case DHCP6_SOLICIT:
|
||||
|
|
@ -951,8 +974,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
|
|||
break;
|
||||
|
||||
default:
|
||||
log_dhcp6_client(client, "unknown message type %d",
|
||||
message->type);
|
||||
log_dhcp6_client(client, "Unknown message type %d", message->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1011,10 +1033,9 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (r >= 0) {
|
||||
if (r >= 0)
|
||||
log_dhcp6_client(client, "Recv %s",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1026,7 +1047,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(client->event, -EINVAL);
|
||||
assert_return(client->index > 0, -EINVAL);
|
||||
assert_return(client->ifindex > 0, -EINVAL);
|
||||
assert_return(client->state != state, -EINVAL);
|
||||
|
||||
client->timeout_resend_expire =
|
||||
|
|
@ -1067,7 +1088,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
if (client->lease->ia.lifetime_t1 == 0xffffffff ||
|
||||
client->lease->ia.lifetime_t2 == 0xffffffff) {
|
||||
|
||||
log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
|
||||
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
|
||||
be32toh(client->lease->ia.lifetime_t1),
|
||||
be32toh(client->lease->ia.lifetime_t2));
|
||||
|
||||
|
|
@ -1159,12 +1180,12 @@ int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
|
|||
}
|
||||
|
||||
int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
||||
int r = 0;
|
||||
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
|
||||
int r = 0;
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(client->event, -EINVAL);
|
||||
assert_return(client->index > 0, -EINVAL);
|
||||
assert_return(client->ifindex > 0, -EINVAL);
|
||||
assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
|
||||
|
||||
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
|
||||
|
|
@ -1182,9 +1203,14 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dhcp6_network_bind_udp_socket(client->index, &client->local_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
|
||||
if (r < 0) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
|
||||
return log_dhcp6_client_errno(client, r,
|
||||
"Failed to bind to UDP socket at address %s: %m", strna(p));
|
||||
}
|
||||
|
||||
client->fd = r;
|
||||
|
||||
|
|
@ -1200,7 +1226,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
|||
goto error;
|
||||
|
||||
r = sd_event_source_set_description(client->receive_message,
|
||||
"dhcp6-receive-message");
|
||||
"dhcp6-receive-message");
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
|
|
@ -1208,8 +1234,8 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
|||
state = DHCP6_STATE_INFORMATION_REQUEST;
|
||||
|
||||
log_dhcp6_client(client, "Started in %s mode",
|
||||
client->information_request? "Information request":
|
||||
"Managed");
|
||||
client->information_request? "Information request":
|
||||
"Managed");
|
||||
|
||||
return client_start(client, state);
|
||||
|
||||
|
|
@ -1246,8 +1272,7 @@ int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
|
|||
}
|
||||
|
||||
sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
|
||||
if (!client)
|
||||
return NULL;
|
||||
assert_return(client, NULL);
|
||||
|
||||
return client->event;
|
||||
}
|
||||
|
|
@ -1295,15 +1320,11 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
|
|||
return -ENOMEM;
|
||||
|
||||
client->n_ref = 1;
|
||||
|
||||
client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
|
||||
|
||||
client->index = -1;
|
||||
|
||||
client->ifindex = -1;
|
||||
client->fd = -1;
|
||||
|
||||
client->req_opts_len = ELEMENTSOF(default_req_opts);
|
||||
|
||||
client->req_opts = new0(be16_t, client->req_opts_len);
|
||||
if (!client->req_opts)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -30,45 +30,30 @@
|
|||
|
||||
#include "alloc-util.h"
|
||||
#include "arp-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "list.h"
|
||||
#include "random-util.h"
|
||||
#include "refcnt.h"
|
||||
#include "siphash24.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Constants from the RFC */
|
||||
#define PROBE_WAIT 1
|
||||
#define PROBE_NUM 3
|
||||
#define PROBE_MIN 1
|
||||
#define PROBE_MAX 2
|
||||
#define ANNOUNCE_WAIT 2
|
||||
#define ANNOUNCE_NUM 2
|
||||
#define ANNOUNCE_INTERVAL 2
|
||||
#define MAX_CONFLICTS 10
|
||||
#define RATE_LIMIT_INTERVAL 60
|
||||
#define DEFEND_INTERVAL 10
|
||||
|
||||
#define IPV4ACD_NETWORK 0xA9FE0000L
|
||||
#define IPV4ACD_NETMASK 0xFFFF0000L
|
||||
|
||||
#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
|
||||
|
||||
#define log_ipv4acd_debug(ll, ...) log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_info(ll, ...) log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_notice(ll, ...) log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_error(ll, ...) log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
|
||||
|
||||
#define log_ipv4acd_debug_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_info_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_notice_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
|
||||
#define log_ipv4acd_error_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
|
||||
#define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
|
||||
#define PROBE_NUM 3U
|
||||
#define PROBE_MIN_USEC (1U * USEC_PER_SEC)
|
||||
#define PROBE_MAX_USEC (2U * USEC_PER_SEC)
|
||||
#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
|
||||
#define ANNOUNCE_NUM 2U
|
||||
#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
|
||||
#define MAX_CONFLICTS 10U
|
||||
#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
|
||||
#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
|
||||
|
||||
typedef enum IPv4ACDState {
|
||||
IPV4ACD_STATE_INIT,
|
||||
IPV4ACD_STATE_STARTED,
|
||||
IPV4ACD_STATE_WAITING_PROBE,
|
||||
IPV4ACD_STATE_PROBING,
|
||||
IPV4ACD_STATE_WAITING_ANNOUNCE,
|
||||
|
|
@ -79,156 +64,164 @@ typedef enum IPv4ACDState {
|
|||
} IPv4ACDState;
|
||||
|
||||
struct sd_ipv4acd {
|
||||
RefCount n_ref;
|
||||
unsigned n_ref;
|
||||
|
||||
IPv4ACDState state;
|
||||
int index;
|
||||
int ifindex;
|
||||
int fd;
|
||||
int iteration;
|
||||
int conflict;
|
||||
sd_event_source *receive_message;
|
||||
sd_event_source *timer;
|
||||
|
||||
unsigned n_iteration;
|
||||
unsigned n_conflict;
|
||||
|
||||
sd_event_source *receive_message_event_source;
|
||||
sd_event_source *timer_event_source;
|
||||
|
||||
usec_t defend_window;
|
||||
be32_t address;
|
||||
|
||||
/* External */
|
||||
struct ether_addr mac_addr;
|
||||
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
sd_ipv4acd_callback_t cb;
|
||||
sd_ipv4acd_callback_t callback;
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
|
||||
if (ll)
|
||||
assert_se(REFCNT_INC(ll->n_ref) >= 2);
|
||||
#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
|
||||
#define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
return ll;
|
||||
static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
|
||||
assert(acd);
|
||||
assert(st < _IPV4ACD_STATE_MAX);
|
||||
|
||||
if (st == acd->state && !reset_counter)
|
||||
acd->n_iteration++;
|
||||
else {
|
||||
acd->state = st;
|
||||
acd->n_iteration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
|
||||
if (!ll || REFCNT_DEC(ll->n_ref) > 0)
|
||||
static void ipv4acd_reset(sd_ipv4acd *acd) {
|
||||
assert(acd);
|
||||
|
||||
acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
|
||||
acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
|
||||
|
||||
acd->fd = safe_close(acd->fd);
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
|
||||
}
|
||||
|
||||
sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) {
|
||||
if (!acd)
|
||||
return NULL;
|
||||
|
||||
ll->receive_message = sd_event_source_unref(ll->receive_message);
|
||||
ll->fd = safe_close(ll->fd);
|
||||
assert_se(acd->n_ref >= 1);
|
||||
acd->n_ref++;
|
||||
|
||||
ll->timer = sd_event_source_unref(ll->timer);
|
||||
return acd;
|
||||
}
|
||||
|
||||
sd_ipv4acd_detach_event(ll);
|
||||
sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) {
|
||||
if (!acd)
|
||||
return NULL;
|
||||
|
||||
free(ll);
|
||||
assert_se(acd->n_ref >= 1);
|
||||
acd->n_ref--;
|
||||
|
||||
if (acd->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
ipv4acd_reset(acd);
|
||||
sd_ipv4acd_detach_event(acd);
|
||||
|
||||
free(acd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_new(sd_ipv4acd **ret) {
|
||||
_cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
|
||||
_cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
ll = new0(sd_ipv4acd, 1);
|
||||
if (!ll)
|
||||
acd = new0(sd_ipv4acd, 1);
|
||||
if (!acd)
|
||||
return -ENOMEM;
|
||||
|
||||
ll->n_ref = REFCNT_INIT;
|
||||
ll->state = IPV4ACD_STATE_INIT;
|
||||
ll->index = -1;
|
||||
ll->fd = -1;
|
||||
acd->n_ref = 1;
|
||||
acd->state = IPV4ACD_STATE_INIT;
|
||||
acd->ifindex = -1;
|
||||
acd->fd = -1;
|
||||
|
||||
*ret = ll;
|
||||
ll = NULL;
|
||||
*ret = acd;
|
||||
acd = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
|
||||
static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
|
||||
assert(acd);
|
||||
|
||||
assert(ll);
|
||||
assert(st < _IPV4ACD_STATE_MAX);
|
||||
if (!acd->callback)
|
||||
return;
|
||||
|
||||
if (st == ll->state && !reset_counter)
|
||||
ll->iteration++;
|
||||
else {
|
||||
ll->state = st;
|
||||
ll->iteration = 0;
|
||||
}
|
||||
acd->callback(acd, event, acd->userdata);
|
||||
}
|
||||
|
||||
static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
|
||||
assert(ll);
|
||||
int sd_ipv4acd_stop(sd_ipv4acd *acd) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
if (ll->cb)
|
||||
ll->cb(ll, event, ll->userdata);
|
||||
}
|
||||
ipv4acd_reset(acd);
|
||||
|
||||
static void ipv4acd_stop(sd_ipv4acd *ll) {
|
||||
assert(ll);
|
||||
log_ipv4acd(acd, "STOPPED");
|
||||
|
||||
ll->receive_message = sd_event_source_unref(ll->receive_message);
|
||||
ll->fd = safe_close(ll->fd);
|
||||
|
||||
ll->timer = sd_event_source_unref(ll->timer);
|
||||
|
||||
log_ipv4acd_debug(ll, "STOPPED");
|
||||
|
||||
ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
|
||||
}
|
||||
|
||||
int sd_ipv4acd_stop(sd_ipv4acd *ll) {
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
ipv4acd_stop(ll);
|
||||
|
||||
ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_STOP);
|
||||
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
|
||||
|
||||
static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
|
||||
static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
|
||||
usec_t next_timeout;
|
||||
usec_t time_now;
|
||||
usec_t next_timeout, time_now;
|
||||
int r;
|
||||
|
||||
assert(sec >= 0);
|
||||
assert(random_sec >= 0);
|
||||
assert(ll);
|
||||
assert(acd);
|
||||
|
||||
next_timeout = sec * USEC_PER_SEC;
|
||||
next_timeout = usec;
|
||||
|
||||
if (random_sec)
|
||||
next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
|
||||
if (random_usec > 0)
|
||||
next_timeout += (usec_t) random_u64() % random_usec;
|
||||
|
||||
assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
|
||||
r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
|
||||
time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
|
||||
r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_priority(timer, ll->event_priority);
|
||||
r = sd_event_source_set_priority(timer, acd->event_priority);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_description(timer, "ipv4acd-timer");
|
||||
if (r < 0)
|
||||
return r;
|
||||
(void) sd_event_source_set_description(timer, "ipv4acd-timer");
|
||||
|
||||
ll->timer = sd_event_source_unref(ll->timer);
|
||||
ll->timer = timer;
|
||||
sd_event_source_unref(acd->timer_event_source);
|
||||
acd->timer_event_source = timer;
|
||||
timer = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
|
||||
assert(ll);
|
||||
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
|
||||
assert(acd);
|
||||
assert(arp);
|
||||
|
||||
/* see the BPF */
|
||||
if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
|
||||
if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
|
||||
return true;
|
||||
|
||||
/* the TPA matched instead of the SPA, this is not a conflict */
|
||||
|
|
@ -236,294 +229,300 @@ static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
|
|||
}
|
||||
|
||||
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_ipv4acd *ll = userdata;
|
||||
sd_ipv4acd *acd = userdata;
|
||||
int r = 0;
|
||||
|
||||
assert(ll);
|
||||
assert(acd);
|
||||
|
||||
switch (ll->state) {
|
||||
case IPV4ACD_STATE_INIT:
|
||||
switch (acd->state) {
|
||||
|
||||
ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
|
||||
case IPV4ACD_STATE_STARTED:
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
|
||||
|
||||
if (ll->conflict >= MAX_CONFLICTS) {
|
||||
log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
|
||||
r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
|
||||
if (acd->n_conflict >= MAX_CONFLICTS) {
|
||||
char ts[FORMAT_TIMESPAN_MAX];
|
||||
log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
goto fail;
|
||||
|
||||
ll->conflict = 0;
|
||||
acd->n_conflict = 0;
|
||||
} else {
|
||||
r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
|
||||
r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPV4ACD_STATE_WAITING_PROBE:
|
||||
case IPV4ACD_STATE_PROBING:
|
||||
/* Send a probe */
|
||||
r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
|
||||
r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
|
||||
goto out;
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
|
||||
goto fail;
|
||||
} else {
|
||||
_cleanup_free_ char *address = NULL;
|
||||
union in_addr_union addr = { .in.s_addr = ll->address };
|
||||
union in_addr_union addr = { .in.s_addr = acd->address };
|
||||
|
||||
r = in_addr_to_string(AF_INET, &addr, &address);
|
||||
if (r >= 0)
|
||||
log_ipv4acd_debug(ll, "Probing %s", address);
|
||||
(void) in_addr_to_string(AF_INET, &addr, &address);
|
||||
log_ipv4acd(acd, "Probing %s", strna(address));
|
||||
}
|
||||
|
||||
if (ll->iteration < PROBE_NUM - 2) {
|
||||
ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
|
||||
if (acd->n_iteration < PROBE_NUM - 2) {
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
|
||||
|
||||
r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
|
||||
r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
|
||||
if (r < 0)
|
||||
goto out;
|
||||
goto fail;
|
||||
} else {
|
||||
ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
|
||||
|
||||
r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
|
||||
r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPV4ACD_STATE_ANNOUNCING:
|
||||
if (ll->iteration >= ANNOUNCE_NUM - 1) {
|
||||
ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
|
||||
|
||||
if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
case IPV4ACD_STATE_WAITING_ANNOUNCE:
|
||||
/* Send announcement packet */
|
||||
r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
|
||||
goto out;
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
|
||||
goto fail;
|
||||
} else
|
||||
log_ipv4acd_debug(ll, "ANNOUNCE");
|
||||
log_ipv4acd(acd, "ANNOUNCE");
|
||||
|
||||
ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
|
||||
|
||||
r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
|
||||
r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
goto fail;
|
||||
|
||||
if (ll->iteration == 0) {
|
||||
ll->conflict = 0;
|
||||
ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
|
||||
if (acd->n_iteration == 0) {
|
||||
acd->n_conflict = 0;
|
||||
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid state.");
|
||||
}
|
||||
|
||||
out:
|
||||
if (r < 0)
|
||||
sd_ipv4acd_stop(ll);
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
sd_ipv4acd_stop(acd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
|
||||
static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
|
||||
_cleanup_free_ char *address = NULL;
|
||||
union in_addr_union addr = { .in.s_addr = ll->address };
|
||||
int r;
|
||||
union in_addr_union addr = { .in.s_addr = acd->address };
|
||||
|
||||
assert(ll);
|
||||
assert(acd);
|
||||
|
||||
ll->conflict++;
|
||||
acd->n_conflict++;
|
||||
|
||||
r = in_addr_to_string(AF_INET, &addr, &address);
|
||||
if (r >= 0)
|
||||
log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
|
||||
(void) in_addr_to_string(AF_INET, &addr, &address);
|
||||
log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
|
||||
|
||||
ipv4acd_stop(ll);
|
||||
|
||||
ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
|
||||
ipv4acd_reset(acd);
|
||||
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
|
||||
}
|
||||
|
||||
static int ipv4acd_on_packet(sd_event_source *s, int fd,
|
||||
uint32_t revents, void *userdata) {
|
||||
sd_ipv4acd *ll = userdata;
|
||||
static int ipv4acd_on_packet(
|
||||
sd_event_source *s,
|
||||
int fd,
|
||||
uint32_t revents,
|
||||
void *userdata) {
|
||||
|
||||
sd_ipv4acd *acd = userdata;
|
||||
struct ether_arp packet;
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
assert(ll);
|
||||
assert(s);
|
||||
assert(acd);
|
||||
assert(fd >= 0);
|
||||
|
||||
r = read(fd, &packet, sizeof(struct ether_arp));
|
||||
if (r < (int) sizeof(struct ether_arp))
|
||||
goto out;
|
||||
n = recv(fd, &packet, sizeof(struct ether_arp), 0);
|
||||
if (n < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
|
||||
log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
|
||||
goto fail;
|
||||
}
|
||||
if ((size_t) n != sizeof(struct ether_arp)) {
|
||||
log_ipv4acd(acd, "Ignoring too short ARP packet.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (acd->state) {
|
||||
|
||||
switch (ll->state) {
|
||||
case IPV4ACD_STATE_ANNOUNCING:
|
||||
case IPV4ACD_STATE_RUNNING:
|
||||
if (ipv4acd_arp_conflict(ll, &packet)) {
|
||||
|
||||
if (ipv4acd_arp_conflict(acd, &packet)) {
|
||||
usec_t ts;
|
||||
|
||||
assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
|
||||
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
|
||||
|
||||
/* Defend address */
|
||||
if (ts > ll->defend_window) {
|
||||
ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
|
||||
r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
|
||||
if (ts > acd->defend_window) {
|
||||
acd->defend_window = ts + DEFEND_INTERVAL_USEC;
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
|
||||
goto out;
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
|
||||
goto fail;
|
||||
} else
|
||||
log_ipv4acd_debug(ll, "DEFEND");
|
||||
log_ipv4acd(acd, "DEFEND");
|
||||
|
||||
} else
|
||||
ipv4acd_on_conflict(ll);
|
||||
ipv4acd_on_conflict(acd);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPV4ACD_STATE_WAITING_PROBE:
|
||||
case IPV4ACD_STATE_PROBING:
|
||||
case IPV4ACD_STATE_WAITING_ANNOUNCE:
|
||||
/* BPF ensures this packet indicates a conflict */
|
||||
ipv4acd_on_conflict(ll);
|
||||
|
||||
ipv4acd_on_conflict(acd);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid state.");
|
||||
}
|
||||
|
||||
out:
|
||||
if (r < 0)
|
||||
sd_ipv4acd_stop(ll);
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
sd_ipv4acd_stop(acd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(interface_index > 0, -EINVAL);
|
||||
assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
|
||||
ll->index = interface_index;
|
||||
acd->ifindex = ifindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
|
||||
assert_return(ll, -EINVAL);
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
|
||||
memcpy(&ll->mac_addr, addr, ETH_ALEN);
|
||||
acd->mac_addr = *addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
|
||||
assert_return(ll, -EINVAL);
|
||||
int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
ll->event = sd_event_unref(ll->event);
|
||||
acd->event = sd_event_unref(acd->event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority) {
|
||||
int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(!ll->event, -EBUSY);
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(!acd->event, -EBUSY);
|
||||
|
||||
if (event)
|
||||
ll->event = sd_event_ref(event);
|
||||
acd->event = sd_event_ref(event);
|
||||
else {
|
||||
r = sd_event_default(&ll->event);
|
||||
r = sd_event_default(&acd->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
ll->event_priority = priority;
|
||||
acd->event_priority = priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata) {
|
||||
assert_return(ll, -EINVAL);
|
||||
int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
ll->cb = cb;
|
||||
ll->userdata = userdata;
|
||||
acd->callback = cb;
|
||||
acd->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address) {
|
||||
assert_return(ll, -EINVAL);
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
|
||||
ll->address = address->s_addr;
|
||||
acd->address = address->s_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_is_running(sd_ipv4acd *ll) {
|
||||
assert_return(ll, false);
|
||||
int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
|
||||
assert_return(acd, false);
|
||||
|
||||
return ll->state != IPV4ACD_STATE_INIT;
|
||||
return acd->state != IPV4ACD_STATE_INIT;
|
||||
}
|
||||
|
||||
static bool ether_addr_is_nul(const struct ether_addr *addr) {
|
||||
const struct ether_addr nul_addr = {};
|
||||
|
||||
assert(addr);
|
||||
|
||||
return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
|
||||
}
|
||||
|
||||
#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
|
||||
|
||||
int sd_ipv4acd_start(sd_ipv4acd *ll) {
|
||||
int sd_ipv4acd_start(sd_ipv4acd *acd) {
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(ll->event, -EINVAL);
|
||||
assert_return(ll->index > 0, -EINVAL);
|
||||
assert_return(ll->address != 0, -EINVAL);
|
||||
assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
|
||||
assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(acd->event, -EINVAL);
|
||||
assert_return(acd->ifindex > 0, -EINVAL);
|
||||
assert_return(acd->address != 0, -EINVAL);
|
||||
assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
|
||||
ll->defend_window = 0;
|
||||
|
||||
r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
|
||||
r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
ll->fd = safe_close(ll->fd);
|
||||
ll->fd = r;
|
||||
|
||||
r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
|
||||
EPOLLIN, ipv4acd_on_packet, ll);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ipv4acd_set_next_wakeup(ll, 0, 0);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
out:
|
||||
if (r < 0) {
|
||||
ipv4acd_stop(ll);
|
||||
return r;
|
||||
}
|
||||
|
||||
safe_close(acd->fd);
|
||||
acd->fd = r;
|
||||
acd->defend_window = 0;
|
||||
acd->n_conflict = 0;
|
||||
|
||||
r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, 0, 0);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ipv4acd_reset(acd);
|
||||
return r;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,16 +30,17 @@
|
|||
#include "sd-ipv4ll.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "list.h"
|
||||
#include "random-util.h"
|
||||
#include "refcnt.h"
|
||||
#include "siphash24.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
#define IPV4LL_NETWORK 0xA9FE0000L
|
||||
#define IPV4LL_NETMASK 0xFFFF0000L
|
||||
#define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
|
||||
#define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
|
||||
|
||||
#define IPV4LL_DONT_DESTROY(ll) \
|
||||
_cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
|
||||
|
|
@ -48,16 +49,28 @@ struct sd_ipv4ll {
|
|||
unsigned n_ref;
|
||||
|
||||
sd_ipv4acd *acd;
|
||||
|
||||
be32_t address; /* the address pushed to ACD */
|
||||
struct random_data *random_data;
|
||||
char *random_data_state;
|
||||
struct ether_addr mac;
|
||||
|
||||
struct {
|
||||
le64_t value;
|
||||
le64_t generation;
|
||||
} seed;
|
||||
bool seed_set;
|
||||
|
||||
/* External */
|
||||
be32_t claimed_address;
|
||||
sd_ipv4ll_callback_t cb;
|
||||
|
||||
sd_ipv4ll_callback_t callback;
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__)
|
||||
#define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
|
||||
|
||||
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
|
||||
if (!ll)
|
||||
return NULL;
|
||||
|
|
@ -79,16 +92,11 @@ sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
|
|||
return NULL;
|
||||
|
||||
sd_ipv4acd_unref(ll->acd);
|
||||
|
||||
free(ll->random_data);
|
||||
free(ll->random_data_state);
|
||||
free(ll);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
|
||||
|
||||
int sd_ipv4ll_new(sd_ipv4ll **ret) {
|
||||
_cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
|
||||
int r;
|
||||
|
|
@ -116,44 +124,32 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
|
|||
}
|
||||
|
||||
int sd_ipv4ll_stop(sd_ipv4ll *ll) {
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
r = sd_ipv4acd_stop(ll->acd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
return sd_ipv4acd_stop(ll->acd);
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
|
||||
int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) {
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
|
||||
|
||||
return sd_ipv4acd_set_index(ll->acd, interface_index);
|
||||
return sd_ipv4acd_set_ifindex(ll->acd, ifindex);
|
||||
}
|
||||
|
||||
#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
|
||||
|
||||
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
|
||||
|
||||
if (!ll->random_data) {
|
||||
uint64_t seed;
|
||||
r = sd_ipv4acd_set_mac(ll->acd, addr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If no random data is set, generate some from the MAC */
|
||||
seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes);
|
||||
|
||||
assert_cc(sizeof(unsigned) <= 8);
|
||||
|
||||
r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_ipv4acd_set_mac(ll->acd, addr);
|
||||
ll->mac = *addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
|
||||
|
|
@ -163,21 +159,15 @@ int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
|
|||
}
|
||||
|
||||
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
r = sd_ipv4acd_attach_event(ll->acd, event, priority);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
return sd_ipv4acd_attach_event(ll->acd, event, priority);
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
ll->cb = cb;
|
||||
ll->callback = cb;
|
||||
ll->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
|
|
@ -195,32 +185,12 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
|
||||
_cleanup_free_ struct random_data *random_data = NULL;
|
||||
_cleanup_free_ char *random_data_state = NULL;
|
||||
int r;
|
||||
|
||||
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
|
||||
|
||||
random_data = new0(struct random_data, 1);
|
||||
if (!random_data)
|
||||
return -ENOMEM;
|
||||
|
||||
random_data_state = new0(char, 128);
|
||||
if (!random_data_state)
|
||||
return -ENOMEM;
|
||||
|
||||
r = initstate_r(seed, random_data_state, 128, random_data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
free(ll->random_data);
|
||||
ll->random_data = random_data;
|
||||
random_data = NULL;
|
||||
|
||||
free(ll->random_data_state);
|
||||
ll->random_data_state = random_data_state;
|
||||
random_data_state = NULL;
|
||||
ll->seed.value = htole64(seed);
|
||||
ll->seed_set = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -232,20 +202,12 @@ int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
|
|||
}
|
||||
|
||||
static bool ipv4ll_address_is_valid(const struct in_addr *address) {
|
||||
uint32_t addr;
|
||||
|
||||
assert(address);
|
||||
|
||||
if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
|
||||
return false;
|
||||
|
||||
addr = be32toh(address->s_addr);
|
||||
|
||||
if ((addr & 0x0000FF00) == 0x0000 ||
|
||||
(addr & 0x0000FF00) == 0xFF00)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
|
||||
|
|
@ -264,48 +226,67 @@ int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
|
||||
|
||||
static int ipv4ll_pick_address(sd_ipv4ll *ll) {
|
||||
struct in_addr in_addr;
|
||||
_cleanup_free_ char *address = NULL;
|
||||
be32_t addr;
|
||||
int r;
|
||||
int32_t random;
|
||||
|
||||
assert(ll);
|
||||
assert(ll->random_data);
|
||||
|
||||
do {
|
||||
r = random_r(ll->random_data, &random);
|
||||
if (r < 0)
|
||||
return r;
|
||||
addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
|
||||
uint64_t h;
|
||||
|
||||
h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes);
|
||||
|
||||
/* Increase the generation counter by one */
|
||||
ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1);
|
||||
|
||||
addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK);
|
||||
} while (addr == ll->address ||
|
||||
(ntohl(addr) & 0x0000FF00) == 0x0000 ||
|
||||
(ntohl(addr) & 0x0000FF00) == 0xFF00);
|
||||
IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U));
|
||||
|
||||
in_addr.s_addr = addr;
|
||||
(void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address);
|
||||
log_ipv4ll(ll, "Picked new IP address %s.", strna(address));
|
||||
|
||||
r = sd_ipv4ll_set_address(ll, &in_addr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
|
||||
}
|
||||
|
||||
#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
|
||||
|
||||
int sd_ipv4ll_start(sd_ipv4ll *ll) {
|
||||
int r;
|
||||
bool picked_address = false;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(ll->random_data, -EINVAL);
|
||||
assert_return(!ether_addr_is_null(&ll->mac), -EINVAL);
|
||||
assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
|
||||
|
||||
/* If no random seed is set, generate some from the MAC address */
|
||||
if (!ll->seed_set)
|
||||
ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes));
|
||||
|
||||
/* Restart the generation counter. */
|
||||
ll->seed.generation = 0;
|
||||
|
||||
if (ll->address == 0) {
|
||||
r = ipv4ll_pick_address(ll);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
picked_address = true;
|
||||
}
|
||||
|
||||
r = sd_ipv4acd_start(ll->acd);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
|
||||
/* We couldn't start? If so, let's forget the picked address again, the user might make a change and
|
||||
* retry, and we want the new data to take effect when picking an address. */
|
||||
if (picked_address)
|
||||
ll->address = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -313,8 +294,8 @@ int sd_ipv4ll_start(sd_ipv4ll *ll) {
|
|||
static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
|
||||
assert(ll);
|
||||
|
||||
if (ll->cb)
|
||||
ll->cb(ll, event, ll->userdata);
|
||||
if (ll->callback)
|
||||
ll->callback(ll, event, ll->userdata);
|
||||
}
|
||||
|
||||
void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
|
||||
|
|
@ -326,17 +307,17 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
|
|||
assert(ll);
|
||||
|
||||
switch (event) {
|
||||
|
||||
case SD_IPV4ACD_EVENT_STOP:
|
||||
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
|
||||
|
||||
ll->claimed_address = 0;
|
||||
|
||||
break;
|
||||
|
||||
case SD_IPV4ACD_EVENT_BIND:
|
||||
ll->claimed_address = ll->address;
|
||||
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
|
||||
|
||||
break;
|
||||
|
||||
case SD_IPV4ACD_EVENT_CONFLICT:
|
||||
/* if an address was already bound we must call up to the
|
||||
user to handle this, otherwise we just try again */
|
||||
|
|
@ -355,6 +336,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
|
|||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid IPv4ACD event.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ int sd_dhcp_client_set_request_address(
|
|||
int sd_dhcp_client_set_request_broadcast(
|
||||
sd_dhcp_client *client,
|
||||
int broadcast);
|
||||
int sd_dhcp_client_set_index(
|
||||
int sd_dhcp_client_set_ifindex(
|
||||
sd_dhcp_client *client,
|
||||
int interface_index);
|
||||
int sd_dhcp_client_set_mac(
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ int sd_dhcp6_client_set_callback(
|
|||
sd_dhcp6_client_callback_t cb,
|
||||
void *userdata);
|
||||
|
||||
int sd_dhcp6_client_set_index(
|
||||
int sd_dhcp6_client_set_ifindex(
|
||||
sd_dhcp6_client *client,
|
||||
int interface_index);
|
||||
int sd_dhcp6_client_set_local_address(
|
||||
|
|
|
|||
|
|
@ -37,20 +37,20 @@ enum {
|
|||
};
|
||||
|
||||
typedef struct sd_ipv4acd sd_ipv4acd;
|
||||
typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *ll, int event, void *userdata);
|
||||
typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata);
|
||||
|
||||
int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
|
||||
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority);
|
||||
int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
|
||||
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata);
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
|
||||
int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
|
||||
int sd_ipv4acd_is_running(sd_ipv4acd *ll);
|
||||
int sd_ipv4acd_start(sd_ipv4acd *ll);
|
||||
int sd_ipv4acd_stop(sd_ipv4acd *ll);
|
||||
sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
|
||||
sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
|
||||
int sd_ipv4acd_detach_event(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority);
|
||||
int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address);
|
||||
int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata);
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
|
||||
int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address);
|
||||
int sd_ipv4acd_is_running(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_start(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_stop(sd_ipv4acd *acd);
|
||||
sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd);
|
||||
sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_new(sd_ipv4acd **ret);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref);
|
||||
|
|
|
|||
|
|
@ -43,15 +43,15 @@ int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
|
|||
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
|
||||
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
|
||||
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
|
||||
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
|
||||
int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
|
||||
int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
|
||||
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
|
||||
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed);
|
||||
int sd_ipv4ll_is_running(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_start(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_stop(sd_ipv4ll *ll);
|
||||
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
|
||||
sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_new (sd_ipv4ll **ret);
|
||||
int sd_ipv4ll_new(sd_ipv4ll **ret);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref);
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ int sd_ndisc_set_callback(sd_ndisc *nd,
|
|||
sd_ndisc_prefix_autonomous_callback_t pacb,
|
||||
sd_ndisc_callback_t cb,
|
||||
void *userdata);
|
||||
int sd_ndisc_set_index(sd_ndisc *nd, int interface_index);
|
||||
int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
|
||||
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
|
||||
|
||||
int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue