merge: merge branch 'systemd' into master

This commit is contained in:
Beniamino Galvani 2015-10-07 10:07:17 +02:00 committed by Thomas Haller
commit 90fb64024c
54 changed files with 7102 additions and 1083 deletions

View file

@ -53,6 +53,7 @@ dnl
dnl Checks for typedefs, structures, and compiler characteristics.
dnl
AC_TYPE_PID_T
AC_CHECK_SIZEOF(dev_t)
dnl
dnl translation support

View file

@ -59,6 +59,7 @@ noinst_LTLIBRARIES = \
SYSTEMD_NM_CFLAGS_PATHS = \
-I$(top_srcdir)/src/systemd/src/systemd \
-I$(top_srcdir)/src/systemd/src/libsystemd-network \
-I$(top_srcdir)/src/systemd/src/libsystemd/sd-event \
-I$(top_srcdir)/src/systemd/src/basic \
-I$(top_srcdir)/src/systemd
@ -68,6 +69,8 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/async.h \
systemd/src/basic/fileio.c \
systemd/src/basic/fileio.h \
systemd/src/basic/hashmap.c \
systemd/src/basic/hashmap.h \
systemd/src/basic/hostname-util.c \
systemd/src/basic/hostname-util.h \
systemd/src/basic/in-addr-util.c \
@ -75,13 +78,18 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/list.h \
systemd/src/basic/log.h \
systemd/src/basic/macro.h \
systemd/src/basic/mempool.c \
systemd/src/basic/mempool.h \
systemd/src/basic/path-util.c \
systemd/src/basic/path-util.h \
systemd/src/basic/prioq.c \
systemd/src/basic/prioq.h \
systemd/src/basic/random-util.c \
systemd/src/basic/random-util.h \
systemd/src/basic/refcnt.h \
systemd/src/basic/siphash24.c \
systemd/src/basic/siphash24.h \
systemd/src/basic/set.h \
systemd/src/basic/socket-util.h \
systemd/src/basic/sparse-endian.h \
systemd/src/basic/strv.c \
@ -93,6 +101,8 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/utf8.h \
systemd/src/basic/util.c \
systemd/src/basic/util.h \
systemd/src/libsystemd-network/arp-util.c \
systemd/src/libsystemd-network/arp-util.h \
systemd/src/libsystemd-network/dhcp-identifier.c \
systemd/src/libsystemd-network/dhcp-identifier.h \
systemd/src/libsystemd-network/dhcp-internal.h \
@ -106,17 +116,27 @@ libsystemd_nm_la_SOURCES = \
systemd/src/libsystemd-network/dhcp6-network.c \
systemd/src/libsystemd-network/dhcp6-option.c \
systemd/src/libsystemd-network/dhcp6-protocol.h \
systemd/src/libsystemd-network/ipv4ll-internal.h \
systemd/src/libsystemd-network/ipv4ll-network.c \
systemd/src/libsystemd-network/ipv4ll-packet.c \
systemd/src/libsystemd-network/lldp.h \
systemd/src/libsystemd-network/lldp-network.h \
systemd/src/libsystemd-network/lldp-network.c \
systemd/src/libsystemd-network/lldp-tlv.c \
systemd/src/libsystemd-network/lldp-tlv.h \
systemd/src/libsystemd-network/lldp-port.c \
systemd/src/libsystemd-network/lldp-port.h \
systemd/src/libsystemd-network/lldp-util.h \
systemd/src/libsystemd-network/lldp-internal.h \
systemd/src/libsystemd-network/lldp-internal.c \
systemd/src/libsystemd-network/network-internal.c \
systemd/src/libsystemd-network/network-internal.h \
systemd/src/libsystemd-network/sd-dhcp-client.c \
systemd/src/libsystemd-network/sd-dhcp-lease.c \
systemd/src/libsystemd-network/sd-dhcp6-client.c \
systemd/src/libsystemd-network/sd-dhcp6-lease.c \
systemd/src/libsystemd-network/sd-ipv4acd.c \
systemd/src/libsystemd-network/sd-ipv4ll.c \
systemd/src/libsystemd-network/sd-lldp.c \
systemd/src/libsystemd/sd-id128/sd-id128.c \
systemd/src/libsystemd/sd-event/event-util.h \
systemd/src/shared/dns-domain.c \
systemd/src/shared/dns-domain.h \
systemd/src/systemd/_sd-common.h \
@ -124,9 +144,11 @@ libsystemd_nm_la_SOURCES = \
systemd/src/systemd/sd-dhcp-lease.h \
systemd/src/systemd/sd-dhcp6-client.h \
systemd/src/systemd/sd-dhcp6-lease.h \
systemd/src/systemd/sd-lldp.h \
systemd/src/systemd/sd-event.h \
systemd/src/systemd/sd-icmp6-nd.h \
systemd/src/systemd/sd-id128.h \
systemd/src/systemd/sd-ipv4acd.h \
systemd/src/systemd/sd-ipv4ll.h
libsystemd_nm_la_CPPFLAGS = \

View file

@ -3245,7 +3245,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
return;
switch (event) {
case IPV4LL_EVENT_BIND:
case SD_IPV4LL_EVENT_BIND:
r = sd_ipv4ll_get_address (ll, &address);
if (r < 0) {
_LOGE (LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r);

View file

@ -491,15 +491,15 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
nm_log_dbg (LOGD_DHCP4, "(%s): DHCPv4 client event %d", iface, event);
switch (event) {
case DHCP_EVENT_EXPIRED:
case SD_DHCP_CLIENT_EVENT_EXPIRED:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_EXPIRE, NULL, NULL);
break;
case DHCP_EVENT_STOP:
case SD_DHCP_CLIENT_EVENT_STOP:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
break;
case DHCP_EVENT_RENEW:
case DHCP_EVENT_IP_CHANGE:
case DHCP_EVENT_IP_ACQUIRE:
case SD_DHCP_CLIENT_EVENT_RENEW:
case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
bound4_handle (self);
break;
default:
@ -671,14 +671,14 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
nm_log_dbg (LOGD_DHCP6, "(%s): DHCPv6 client event %d", iface, event);
switch (event) {
case DHCP6_EVENT_RETRANS_MAX:
case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_TIMEOUT, NULL, NULL);
break;
case DHCP6_EVENT_RESEND_EXPIRE:
case DHCP6_EVENT_STOP:
case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
case SD_DHCP6_CLIENT_EVENT_STOP:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
break;
case DHCP6_EVENT_IP_ACQUIRE:
case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
bound6_handle (self);
break;
default:

View file

@ -69,6 +69,12 @@ G_STMT_START { \
g_assert_not_reached (); \
} G_STMT_END
#define log_assert_failed_unreachable(text, file, line, func) \
G_STMT_START { \
log_internal (LOG_CRIT, 0, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", text, file, line, func); \
g_assert_not_reached (); \
} G_STMT_END
#define log_assert_failed_return(text, file, line, func) \
({ \
log_internal (LOG_DEBUG, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", text, file, line, func); \
@ -76,7 +82,6 @@ G_STMT_START { \
(void) 0; \
})
/*****************************************************************************
* The remainder of the header is only enabled when building the systemd code
* itself.
@ -132,6 +137,10 @@ static inline pid_t gettid(void) {
return (pid_t) syscall(SYS_gettid);
}
static inline bool is_main_thread(void) {
return TRUE;
}
#endif /* (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD */
#endif /* NM_SD_ADAPT_H */

View file

@ -777,15 +777,19 @@ int executable_is_script(const char *path, char **interpreter) {
/**
* Retrieve one field from a file like /proc/self/status. pattern
* should start with '\n' and end with a ':'. Whitespace and zeros
* after the ':' will be skipped. field must be freed afterwards.
* should not include whitespace or the delimiter (':'). pattern matches only
* the beginning of a line. Whitespace before ':' is skipped. Whitespace and
* zeros after the ':' will be skipped. field must be freed afterwards.
* terminator specifies the terminating characters of the field value (not
* included in the value).
*/
int get_status_field(const char *filename, const char *pattern, char **field) {
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
_cleanup_free_ char *status = NULL;
char *t, *f;
size_t len;
int r;
assert(terminator);
assert(filename);
assert(pattern);
assert(field);
@ -794,11 +798,31 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
if (r < 0)
return r;
t = strstr(status, pattern);
if (!t)
return -ENOENT;
t = status;
do {
bool pattern_ok;
do {
t = strstr(t, pattern);
if (!t)
return -ENOENT;
/* Check that pattern occurs in beginning of line. */
pattern_ok = (t == status || t[-1] == '\n');
t += strlen(pattern);
} while (!pattern_ok);
t += strspn(t, " \t");
if (!*t)
return -ENOENT;
} while (*t != ':');
t++;
t += strlen(pattern);
if (*t) {
t += strspn(t, " \t");
@ -814,7 +838,7 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
t --;
}
len = strcspn(t, WHITESPACE);
len = strcspn(t, terminator);
f = strndup(t, len);
if (!f)

View file

@ -51,4 +51,4 @@ int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
int get_status_field(const char *filename, const char *pattern, char **field);
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,415 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2014 Michal Schmidt
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <stdbool.h>
#include "macro.h"
#include "siphash24.h"
#include "util.h"
/*
* A hash table implementation. As a minor optimization a NULL hashmap object
* will be treated as empty hashmap for all read operations. That way it is not
* necessary to instantiate an object for each Hashmap use.
*
* If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
* the implemention will:
* - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
* - perform extra checks for invalid use of iterators
*/
#define HASH_KEY_SIZE 16
/* The base type for all hashmap and set types. Many functions in the
* implementation take (HashmapBase*) parameters and are run-time polymorphic,
* though the API is not meant to be polymorphic (do not call functions
* internal_*() directly). */
typedef struct HashmapBase HashmapBase;
/* Specific hashmap/set types */
typedef struct Hashmap Hashmap; /* Maps keys to values */
typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
typedef struct Set Set; /* Stores just keys */
/* Ideally the Iterator would be an opaque struct, but it is instantiated
* by hashmap users, so the definition has to be here. Do not use its fields
* directly. */
typedef struct {
unsigned idx; /* index of an entry to be iterated next */
const void *next_key; /* expected value of that entry's key pointer */
#ifdef ENABLE_DEBUG_HASHMAP
unsigned put_count; /* hashmap's put_count recorded at start of iteration */
unsigned rem_count; /* hashmap's rem_count in previous iteration */
unsigned prev_idx; /* idx in previous iteration */
#endif
} Iterator;
#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
typedef void (*hash_func_t)(const void *p, struct siphash *state);
typedef int (*compare_func_t)(const void *a, const void *b);
struct hash_ops {
hash_func_t hash;
compare_func_t compare;
};
void string_hash_func(const void *p, struct siphash *state);
int string_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops string_hash_ops;
/* This will compare the passed pointers directly, and will not
* dereference them. This is hence not useful for strings or
* suchlike. */
void trivial_hash_func(const void *p, struct siphash *state);
int trivial_compare_func(const void *a, const void *b) _const_;
extern const struct hash_ops trivial_hash_ops;
/* 32bit values we can always just embedd in the pointer itself, but
* in order to support 32bit archs we need store 64bit values
* indirectly, since they don't fit in a pointer. */
void uint64_hash_func(const void *p, struct siphash *state);
int uint64_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
* it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const void *p, struct siphash *state) _pure_;
int devt_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops devt_hash_ops = {
.hash = devt_hash_func,
.compare = devt_compare_func
};
#else
#define devt_hash_func uint64_hash_func
#define devt_compare_func uint64_compare_func
#define devt_hash_ops uint64_hash_ops
#endif
/* Macros for type checking */
#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
(__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
__builtin_types_compatible_p(typeof(h), Hashmap*) || \
__builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
__builtin_types_compatible_p(typeof(h), Set*))
#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
(__builtin_types_compatible_p(typeof(h), Hashmap*) || \
__builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
#define HASHMAP_BASE(h) \
__builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
(HashmapBase*)(h), \
(void)0)
#define PLAIN_HASHMAP(h) \
__builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
(Hashmap*)(h), \
(void)0)
#ifdef ENABLE_DEBUG_HASHMAP
# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
#else
# define HASHMAP_DEBUG_PARAMS
# define HASHMAP_DEBUG_SRC_ARGS
# define HASHMAP_DEBUG_PASS_ARGS
#endif
Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
HashmapBase *internal_hashmap_free(HashmapBase *h);
static inline Hashmap *hashmap_free(Hashmap *h) {
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
HashmapBase *internal_hashmap_free_free(HashmapBase *h);
static inline Hashmap *hashmap_free_free(Hashmap *h) {
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
Hashmap *hashmap_free_free_free(Hashmap *h);
static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
}
HashmapBase *internal_hashmap_copy(HashmapBase *h);
static inline Hashmap *hashmap_copy(Hashmap *h) {
return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
}
int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int hashmap_put(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
return hashmap_put(PLAIN_HASHMAP(h), key, value);
}
int hashmap_update(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
return hashmap_update(PLAIN_HASHMAP(h), key, value);
}
int hashmap_replace(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
return hashmap_replace(PLAIN_HASHMAP(h), key, value);
}
void *internal_hashmap_get(HashmapBase *h, const void *key);
static inline void *hashmap_get(Hashmap *h, const void *key) {
return internal_hashmap_get(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
return internal_hashmap_get(HASHMAP_BASE(h), key);
}
void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
}
bool internal_hashmap_contains(HashmapBase *h, const void *key);
static inline bool hashmap_contains(Hashmap *h, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(h), key);
}
static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(h), key);
}
void *internal_hashmap_remove(HashmapBase *h, const void *key);
static inline void *hashmap_remove(Hashmap *h, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(h), key);
}
void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
}
void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
}
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
}
int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
}
/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
* should just work, allow this by having looser type-checking here. */
int internal_hashmap_merge(Hashmap *h, Hashmap *other);
#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
static inline int hashmap_move(Hashmap *h, Hashmap *other) {
return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
unsigned internal_hashmap_size(HashmapBase *h) _pure_;
static inline unsigned hashmap_size(Hashmap *h) {
return internal_hashmap_size(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
return internal_hashmap_size(HASHMAP_BASE(h));
}
static inline bool hashmap_isempty(Hashmap *h) {
return hashmap_size(h) == 0;
}
static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
return ordered_hashmap_size(h) == 0;
}
unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
static inline unsigned hashmap_buckets(Hashmap *h) {
return internal_hashmap_buckets(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
return internal_hashmap_buckets(HASHMAP_BASE(h));
}
bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) {
return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) {
return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
void internal_hashmap_clear(HashmapBase *h);
static inline void hashmap_clear(Hashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h));
}
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h));
}
void internal_hashmap_clear_free(HashmapBase *h);
static inline void hashmap_clear_free(Hashmap *h) {
internal_hashmap_clear_free(HASHMAP_BASE(h));
}
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
internal_hashmap_clear_free(HASHMAP_BASE(h));
}
void hashmap_clear_free_free(Hashmap *h);
static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
hashmap_clear_free_free(PLAIN_HASHMAP(h));
}
/*
* Note about all *_first*() functions
*
* For plain Hashmaps and Sets the order of entries is undefined.
* The functions find whatever entry is first in the implementation
* internal order.
*
* Only for OrderedHashmaps the order is well defined and finding
* the first entry is O(1).
*/
void *internal_hashmap_steal_first(HashmapBase *h);
static inline void *hashmap_steal_first(Hashmap *h) {
return internal_hashmap_steal_first(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
return internal_hashmap_steal_first(HASHMAP_BASE(h));
}
void *internal_hashmap_steal_first_key(HashmapBase *h);
static inline void *hashmap_steal_first_key(Hashmap *h) {
return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
}
void *internal_hashmap_first_key(HashmapBase *h) _pure_;
static inline void *hashmap_first_key(Hashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h));
}
void *internal_hashmap_first(HashmapBase *h) _pure_;
static inline void *hashmap_first(Hashmap *h) {
return internal_hashmap_first(HASHMAP_BASE(h));
}
static inline void *ordered_hashmap_first(OrderedHashmap *h) {
return internal_hashmap_first(HASHMAP_BASE(h));
}
/* no hashmap_next */
void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
char **internal_hashmap_get_strv(HashmapBase *h);
static inline char **hashmap_get_strv(Hashmap *h) {
return internal_hashmap_get_strv(HASHMAP_BASE(h));
}
static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
return internal_hashmap_get_strv(HASHMAP_BASE(h));
}
/*
* Hashmaps are iterated in unpredictable order.
* OrderedHashmaps are an exception to this. They are iterated in the order
* the entries were inserted.
* It is safe to remove the current entry.
*/
#define HASHMAP_FOREACH(e, h, i) \
for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); )
#define ORDERED_HASHMAP_FOREACH(e, h, i) \
for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); )
#define HASHMAP_FOREACH_KEY(e, k, h, i) \
for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)

View file

@ -231,3 +231,15 @@ int log_syntax_internal(
? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
({ \
int _level = (level); \
if (log_get_max_level() >= LOG_PRI(_level)) { \
_cleanup_free_ char *_p = NULL; \
_p = utf8_escape_invalid(rvalue); \
log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
"String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
} \
-EINVAL; \
})

View file

@ -125,8 +125,11 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
#define ELEMENTSOF(x) \
__extension__ (__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
sizeof(x)/sizeof((x)[0]), \
(void)0))
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
@ -215,18 +218,20 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
(__x / __y + !!(__x % __y)); \
})
#define assert_se(expr) \
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false) \
log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
#define assert_se(expr) assert_message_se(expr, #expr)
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) do {} while(false)
#else
#define assert(expr) assert_se(expr)
#define assert(expr) assert_message_se(expr, #expr)
#endif
#define assert_not_reached(t) \
@ -251,19 +256,19 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
REENABLE_WARNING
#endif
#define assert_log(expr) ((_likely_(expr)) \
? (true) \
: (log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
#define assert_log(expr, message) ((_likely_(expr)) \
? (true) \
: (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
#define assert_return(expr, r) \
do { \
if (!assert_log(expr)) \
if (!assert_log(expr, #expr)) \
return (r); \
} while (false)
#define assert_return_errno(expr, r, err) \
do { \
if (!assert_log(expr)) { \
if (!assert_log(expr, #expr)) { \
errno = err; \
return (r); \
} \
@ -469,18 +474,6 @@ do { \
#define MODE_INVALID ((mode_t) -1)
#endif /* NM_IGNORED */
static inline bool UID_IS_INVALID(uid_t uid) {
/* We consider both the old 16bit -1 user and the newer 32bit
* -1 user invalid, since they are or used to be incompatible
* with syscalls such as setresuid() or chown(). */
return uid == (uid_t) ((uint32_t) -1) || uid == (uid_t) ((uint16_t) -1);
}
static inline bool GID_IS_INVALID(gid_t gid) {
return gid == (gid_t) ((uint32_t) -1) || gid == (gid_t) ((uint16_t) -1);
}
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \

View file

@ -0,0 +1,105 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010-2014 Lennart Poettering
Copyright 2014 Michal Schmidt
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "mempool.h"
#include "macro.h"
#include "util.h"
struct pool {
struct pool *next;
unsigned n_tiles;
unsigned n_used;
};
void* mempool_alloc_tile(struct mempool *mp) {
unsigned i;
/* When a tile is released we add it to the list and simply
* place the next pointer at its offset 0. */
assert(mp->tile_size >= sizeof(void*));
assert(mp->at_least > 0);
if (mp->freelist) {
void *r;
r = mp->freelist;
mp->freelist = * (void**) mp->freelist;
return r;
}
if (_unlikely_(!mp->first_pool) ||
_unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) {
unsigned n;
size_t size;
struct pool *p;
n = mp->first_pool ? mp->first_pool->n_tiles : 0;
n = MAX(mp->at_least, n * 2);
size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size);
n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size;
p = malloc(size);
if (!p)
return NULL;
p->next = mp->first_pool;
p->n_tiles = n;
p->n_used = 0;
mp->first_pool = p;
}
i = mp->first_pool->n_used++;
return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
}
void* mempool_alloc0_tile(struct mempool *mp) {
void *p;
p = mempool_alloc_tile(mp);
if (p)
memzero(p, mp->tile_size);
return p;
}
void mempool_free_tile(struct mempool *mp, void *p) {
* (void**) p = mp->freelist;
mp->freelist = p;
}
#ifdef VALGRIND
void mempool_drop(struct mempool *mp) {
struct pool *p = mp->first_pool;
while (p) {
struct pool *n;
n = p->next;
free(p);
p = n;
}
}
#endif

View file

@ -0,0 +1,51 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2011-2014 Lennart Poettering
Copyright 2014 Michal Schmidt
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <stddef.h>
struct pool;
struct mempool {
struct pool *first_pool;
void *freelist;
size_t tile_size;
unsigned at_least;
};
void* mempool_alloc_tile(struct mempool *mp);
void* mempool_alloc0_tile(struct mempool *mp);
void mempool_free_tile(struct mempool *mp, void *p);
#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
struct mempool pool_name = { \
.tile_size = sizeof(tile_type), \
.at_least = alloc_at_least, \
}
#ifdef VALGRIND
void mempool_drop(struct mempool *mp);
#endif

View file

@ -0,0 +1,320 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
/*
* Priority Queue
* The prioq object implements a priority queue. That is, it orders objects by
* their priority and allows O(1) access to the object with the highest
* priority. Insertion and removal are Θ(log n). Optionally, the caller can
* provide a pointer to an index which will be kept up-to-date by the prioq.
*
* The underlying algorithm used in this implementation is a Heap.
*/
#include "nm-sd-adapt.h"
#include "util.h"
#include "prioq.h"
struct prioq_item {
void *data;
unsigned *idx;
};
struct Prioq {
compare_func_t compare_func;
unsigned n_items, n_allocated;
struct prioq_item *items;
};
Prioq *prioq_new(compare_func_t compare_func) {
Prioq *q;
q = new0(Prioq, 1);
if (!q)
return q;
q->compare_func = compare_func;
return q;
}
Prioq* prioq_free(Prioq *q) {
if (!q)
return NULL;
free(q->items);
free(q);
return NULL;
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
assert(q);
if (*q)
return 0;
*q = prioq_new(compare_func);
if (!*q)
return -ENOMEM;
return 0;
}
static void swap(Prioq *q, unsigned j, unsigned k) {
void *saved_data;
unsigned *saved_idx;
assert(q);
assert(j < q->n_items);
assert(k < q->n_items);
assert(!q->items[j].idx || *(q->items[j].idx) == j);
assert(!q->items[k].idx || *(q->items[k].idx) == k);
saved_data = q->items[j].data;
saved_idx = q->items[j].idx;
q->items[j].data = q->items[k].data;
q->items[j].idx = q->items[k].idx;
q->items[k].data = saved_data;
q->items[k].idx = saved_idx;
if (q->items[j].idx)
*q->items[j].idx = j;
if (q->items[k].idx)
*q->items[k].idx = k;
}
static unsigned shuffle_up(Prioq *q, unsigned idx) {
assert(q);
while (idx > 0) {
unsigned k;
k = (idx-1)/2;
if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
break;
swap(q, idx, k);
idx = k;
}
return idx;
}
static unsigned shuffle_down(Prioq *q, unsigned idx) {
assert(q);
for (;;) {
unsigned j, k, s;
k = (idx+1)*2; /* right child */
j = k-1; /* left child */
if (j >= q->n_items)
break;
if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
/* So our left child is smaller than we are, let's
* remember this fact */
s = j;
else
s = idx;
if (k < q->n_items &&
q->compare_func(q->items[k].data, q->items[s].data) < 0)
/* So our right child is smaller than we are, let's
* remember this fact */
s = k;
/* s now points to the smallest of the three items */
if (s == idx)
/* No swap necessary, we're done */
break;
swap(q, idx, s);
idx = s;
}
return idx;
}
int prioq_put(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
if (q->n_items >= q->n_allocated) {
unsigned n;
struct prioq_item *j;
n = MAX((q->n_items+1) * 2, 16u);
j = realloc(q->items, sizeof(struct prioq_item) * n);
if (!j)
return -ENOMEM;
q->items = j;
q->n_allocated = n;
}
k = q->n_items++;
i = q->items + k;
i->data = data;
i->idx = idx;
if (idx)
*idx = k;
shuffle_up(q, k);
return 0;
}
static void remove_item(Prioq *q, struct prioq_item *i) {
struct prioq_item *l;
assert(q);
assert(i);
l = q->items + q->n_items - 1;
if (i == l)
/* Last entry, let's just remove it */
q->n_items--;
else {
unsigned k;
/* Not last entry, let's replace the last entry with
* this one, and reshuffle */
k = i - q->items;
i->data = l->data;
i->idx = l->idx;
if (i->idx)
*i->idx = k;
q->n_items--;
k = shuffle_down(q, k);
shuffle_up(q, k);
}
}
_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
assert(q);
if (idx) {
if (*idx == PRIOQ_IDX_NULL ||
*idx > q->n_items)
return NULL;
i = q->items + *idx;
if (i->data != data)
return NULL;
return i;
} else {
for (i = q->items; i < q->items + q->n_items; i++)
if (i->data == data)
return i;
return NULL;
}
}
int prioq_remove(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
if (!q)
return 0;
i = find_item(q, data, idx);
if (!i)
return 0;
remove_item(q, i);
return 1;
}
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
i = find_item(q, data, idx);
if (!i)
return 0;
k = i - q->items;
k = shuffle_down(q, k);
shuffle_up(q, k);
return 1;
}
void *prioq_peek(Prioq *q) {
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
return q->items[0].data;
}
void *prioq_pop(Prioq *q) {
void *data;
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
data = q->items[0].data;
remove_item(q, q->items);
return data;
}
unsigned prioq_size(Prioq *q) {
if (!q)
return 0;
return q->n_items;
}
bool prioq_isempty(Prioq *q) {
if (!q)
return true;
return q->n_items <= 0;
}

View file

@ -0,0 +1,44 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "hashmap.h"
typedef struct Prioq Prioq;
#define PRIOQ_IDX_NULL ((unsigned) -1)
Prioq *prioq_new(compare_func_t compare);
Prioq *prioq_free(Prioq *q);
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
int prioq_remove(Prioq *q, void *data, unsigned *idx);
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
void *prioq_peek(Prioq *q) _pure_;
void *prioq_pop(Prioq *q);
unsigned prioq_size(Prioq *q) _pure_;
bool prioq_isempty(Prioq *q) _pure_;

138
src/systemd/src/basic/set.h Normal file
View file

@ -0,0 +1,138 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "hashmap.h"
#include "macro.h"
Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
return NULL;
}
static inline Set *set_free_free(Set *s) {
internal_hashmap_free_free(HASHMAP_BASE(s));
return NULL;
}
/* no set_free_free_free */
static inline Set *set_copy(Set *s) {
return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
}
int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int set_put(Set *s, const void *key);
/* no set_update */
/* no set_replace */
static inline void *set_get(Set *s, void *key) {
return internal_hashmap_get(HASHMAP_BASE(s), key);
}
/* no set_get2 */
static inline bool set_contains(Set *s, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(s), key);
}
static inline void *set_remove(Set *s, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(s), key);
}
/* no set_remove2 */
/* no set_remove_value */
int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
/* no set_remove_and_replace */
int set_merge(Set *s, Set *other);
static inline int set_reserve(Set *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
static inline int set_move(Set *s, Set *other) {
return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
}
static inline int set_move_one(Set *s, Set *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
}
static inline unsigned set_size(Set *s) {
return internal_hashmap_size(HASHMAP_BASE(s));
}
static inline bool set_isempty(Set *s) {
return set_size(s) == 0;
}
static inline unsigned set_buckets(Set *s) {
return internal_hashmap_buckets(HASHMAP_BASE(s));
}
bool set_iterate(Set *s, Iterator *i, void **value);
static inline void set_clear(Set *s) {
internal_hashmap_clear(HASHMAP_BASE(s));
}
static inline void set_clear_free(Set *s) {
internal_hashmap_clear_free(HASHMAP_BASE(s));
}
/* no set_clear_free_free */
static inline void *set_steal_first(Set *s) {
return internal_hashmap_steal_first(HASHMAP_BASE(s));
}
/* no set_steal_first_key */
/* no set_first_key */
static inline void *set_first(Set *s) {
return internal_hashmap_first(HASHMAP_BASE(s));
}
/* no set_next */
static inline char **set_get_strv(Set *s) {
return internal_hashmap_get_strv(HASHMAP_BASE(s));
}
int set_consume(Set *s, void *value);
int set_put_strdup(Set *s, const char *p);
int set_put_strdupv(Set *s, char **l);
#define SET_FOREACH(e, s, i) \
for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
#define _cleanup_set_free_ _cleanup_(set_freep)
#define _cleanup_set_free_free_ _cleanup_(set_free_freep)

View file

@ -13,126 +13,172 @@
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
(Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
(Refactored by Tom Gundersen to split up in several functions and follow systemd
coding style)
*/
#include "nm-sd-adapt.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "sparse-endian.h"
#include "siphash24.h"
#include "util.h"
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint8_t u8;
static inline uint64_t rotate_left(uint64_t x, uint8_t b) {
assert(b < 64);
#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
return (x << b) | (x >> (64 - b));
}
#define U32TO8_LE(p, v) \
(p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
(p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
static inline void sipround(struct siphash *state) {
assert(state);
#define U64TO8_LE(p, v) \
U32TO8_LE((p), (u32)((v) )); \
U32TO8_LE((p) + 4, (u32)((v) >> 32));
state->v0 += state->v1;
state->v1 = rotate_left(state->v1, 13);
state->v1 ^= state->v0;
state->v0 = rotate_left(state->v0, 32);
state->v2 += state->v3;
state->v3 = rotate_left(state->v3, 16);
state->v3 ^= state->v2;
state->v0 += state->v3;
state->v3 = rotate_left(state->v3, 21);
state->v3 ^= state->v0;
state->v2 += state->v1;
state->v1 = rotate_left(state->v1, 17);
state->v1 ^= state->v2;
state->v2 = rotate_left(state->v2, 32);
}
#define U8TO64_LE(p) \
(((u64)((p)[0]) ) | \
((u64)((p)[1]) << 8) | \
((u64)((p)[2]) << 16) | \
((u64)((p)[3]) << 24) | \
((u64)((p)[4]) << 32) | \
((u64)((p)[5]) << 40) | \
((u64)((p)[6]) << 48) | \
((u64)((p)[7]) << 56))
void siphash24_init(struct siphash *state, const uint8_t k[16]) {
uint64_t k0, k1;
#define SIPROUND \
do { \
v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
} while(0)
assert(state);
assert(k);
k0 = le64toh(*(le64_t*) k);
k1 = le64toh(*(le64_t*) (k + 8));
/* "somepseudorandomlygeneratedbytes" */
state->v0 = 0x736f6d6570736575ULL ^ k0;
state->v1 = 0x646f72616e646f6dULL ^ k1;
state->v2 = 0x6c7967656e657261ULL ^ k0;
state->v3 = 0x7465646279746573ULL ^ k1;
state->padding = 0;
state->inlen = 0;
}
void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
uint64_t m;
const uint8_t *in = _in;
const uint8_t *end = in + inlen;
unsigned left = state->inlen & 7;
assert(in);
assert(state);
/* update total length */
state->inlen += inlen;
/* if padding exists, fill it out */
if (left > 0) {
for ( ; in < end && left < 8; in ++, left ++ )
state->padding |= ( ( uint64_t )*in ) << (left * 8);
if (in == end && left < 8)
/* we did not have enough input to fill out the padding completely */
return;
#ifdef DEBUG
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding);
#endif
state->v3 ^= state->padding;
sipround(state);
sipround(state);
state->v0 ^= state->padding;
state->padding = 0;
}
end -= ( state->inlen % sizeof (uint64_t) );
for ( ; in < end; in += 8 ) {
m = le64toh(*(le64_t*) in);
#ifdef DEBUG
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m);
#endif
state->v3 ^= m;
sipround(state);
sipround(state);
state->v0 ^= m;
}
left = state->inlen & 7;
switch(left)
{
case 7: state->padding |= ((uint64_t) in[6]) << 48;
case 6: state->padding |= ((uint64_t) in[5]) << 40;
case 5: state->padding |= ((uint64_t) in[4]) << 32;
case 4: state->padding |= ((uint64_t) in[3]) << 24;
case 3: state->padding |= ((uint64_t) in[2]) << 16;
case 2: state->padding |= ((uint64_t) in[1]) << 8;
case 1: state->padding |= ((uint64_t) in[0]); break;
case 0: break;
}
}
void siphash24_finalize(uint8_t out[8], struct siphash *state) {
uint64_t b;
b = state->padding | (( ( uint64_t )state->inlen ) << 56);
#ifdef DEBUG
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t)state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t)state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t)state->v2);
printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t)state->v3);
printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding);
#endif
state->v3 ^= b;
sipround(state);
sipround(state);
state->v0 ^= b;
#ifdef DEBUG
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
#endif
state->v2 ^= 0xff;
sipround(state);
sipround(state);
sipround(state);
sipround(state);
*(le64_t*)out = htole64(state->v0 ^ state->v1 ^ state->v2 ^ state->v3);
}
/* SipHash-2-4 */
void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16])
{
/* "somepseudorandomlygeneratedbytes" */
u64 v0 = 0x736f6d6570736575ULL;
u64 v1 = 0x646f72616e646f6dULL;
u64 v2 = 0x6c7967656e657261ULL;
u64 v3 = 0x7465646279746573ULL;
u64 b;
u64 k0 = U8TO64_LE( k );
u64 k1 = U8TO64_LE( k + 8 );
u64 m;
const u8 *in = _in;
const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
const int left = inlen & 7;
b = ( ( u64 )inlen ) << 56;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) {
struct siphash state;
for ( ; in != end; in += 8 )
{
m = U8TO64_LE( in );
#ifdef DEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
#endif
v3 ^= m;
SIPROUND;
SIPROUND;
v0 ^= m;
}
switch( left )
{
case 7: b |= ( ( u64 )in[ 6] ) << 48;
case 6: b |= ( ( u64 )in[ 5] ) << 40;
case 5: b |= ( ( u64 )in[ 4] ) << 32;
case 4: b |= ( ( u64 )in[ 3] ) << 24;
case 3: b |= ( ( u64 )in[ 2] ) << 16;
case 2: b |= ( ( u64 )in[ 1] ) << 8;
case 1: b |= ( ( u64 )in[ 0] ); break;
case 0: break;
}
#ifdef DEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
#endif
v3 ^= b;
SIPROUND;
SIPROUND;
v0 ^= b;
#ifdef DEBUG
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
#endif
v2 ^= 0xff;
SIPROUND;
SIPROUND;
SIPROUND;
SIPROUND;
b = v0 ^ v1 ^ v2 ^ v3;
U64TO8_LE( out, b );
siphash24_init(&state, k);
siphash24_compress(_in, inlen, &state);
siphash24_finalize(out, &state);
}

View file

@ -5,4 +5,17 @@
#include <inttypes.h>
#include <sys/types.h>
struct siphash {
uint64_t v0;
uint64_t v1;
uint64_t v2;
uint64_t v3;
uint64_t padding;
size_t inlen;
};
void siphash24_init(struct siphash *state, const uint8_t k[16]);
void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
void siphash24_finalize(uint8_t out[8], struct siphash *state);
void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);

View file

@ -280,8 +280,8 @@ char **strv_split_newlines(const char *s) {
#if 0 /* NM_IGNORED */
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
size_t n = 0, allocated = 0;
int r;
assert(t);
@ -305,13 +305,16 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
l[n] = NULL;
}
if (!l)
if (!l) {
l = new0(char*, 1);
if (!l)
return -ENOMEM;
}
*t = l;
l = NULL;
return 0;
return (int) n;
}
#endif /* NM_IGNORED */
@ -724,3 +727,66 @@ bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
return false;
}
char ***strv_free_free(char ***l) {
char ***i;
if (!l)
return NULL;
for (i = l; *i; i++)
strv_free(*i);
free(l);
return NULL;
}
char **strv_skip(char **l, size_t n) {
while (n > 0) {
if (strv_isempty(l))
return l;
l++, n--;
}
return l;
}
int strv_extend_n(char ***l, const char *value, size_t n) {
size_t i, j, k;
char **nl;
assert(l);
if (!value)
return 0;
if (n == 0)
return 0;
/* Adds the value value n times to l */
k = strv_length(*l);
nl = realloc(*l, sizeof(char*) * (k + n + 1));
if (!nl)
return -ENOMEM;
*l = nl;
for (i = k; i < k + n; i++) {
nl[i] = strdup(value);
if (!nl[i])
goto rollback;
}
nl[i] = NULL;
return 0;
rollback:
for (j = k; j < i; i++)
free(nl[j]);
nl[k] = NULL;
return -ENOMEM;
}

View file

@ -156,3 +156,9 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
return strv_isempty(patterns) ||
strv_fnmatch(patterns, s, flags);
}
char ***strv_free_free(char ***l);
char **strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);

View file

@ -114,6 +114,8 @@ bool timezone_is_valid(const char *name);
clockid_t clock_boottime_or_monotonic(void);
#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0)
#define xstrftime(buf, fmt, tm) \
assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \
"xstrftime: " #buf "[] must be big enough")
int get_timezone(char **timezone);

View file

@ -21,49 +21,48 @@
#include "nm-sd-adapt.h"
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <libintl.h>
#include <stdio.h>
#include <syslog.h>
#include <sched.h>
#include <sys/resource.h>
#include <linux/sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <poll.h>
#include <ctype.h>
#include <sys/prctl.h>
#include <sys/utsname.h>
#include <pwd.h>
#include <netinet/ip.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <grp.h>
#include <sys/mman.h>
#include <sys/vfs.h>
#include <sys/mount.h>
#include <linux/magic.h>
#include <limits.h>
#include <langinfo.h>
#include <libintl.h>
#include <limits.h>
#include <linux/magic.h>
#include <linux/sched.h>
#include <locale.h>
#include <sys/personality.h>
#include <sys/xattr.h>
#include <sys/statvfs.h>
#include <netinet/ip.h>
#include <poll.h>
#include <pwd.h>
#include <sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/vfs.h>
#include <sys/wait.h>
#include <sys/xattr.h>
#include <syslog.h>
#include <unistd.h>
/* When we include libgen.h because we need dirname() we immediately
* undefine basename() since libgen.h defines it as a macro to the POSIX
* version which is really broken. We prefer GNU basename(). */
* undefine basename() since libgen.h defines it as a macro to the
* POSIX version which is really broken. We prefer GNU basename(). */
#include <libgen.h>
#undef basename
@ -71,36 +70,42 @@
#include <sys/auxv.h>
#endif
#include "config.h"
#include "macro.h"
#include "util.h"
/* We include linux/fs.h as last of the system headers, as it
* otherwise conflicts with sys/mount.h. Yay, Linux is great! */
#include <linux/fs.h>
#if 0 /* NM_IGNORED */
#include "build.h"
#include "def.h"
#include "device-nodes.h"
#include "env-util.h"
#include "exit-status.h"
#include "fileio.h"
#include "formats-util.h"
#include "gunicode.h"
#include "hashmap.h"
#include "hostname-util.h"
#include "ioprio.h"
#include "missing.h"
#include "log.h"
#include "strv.h"
#endif /* NM_IGNORED */
#include "macro.h"
#if 0 /* NM_IGNORED */
#include "missing.h"
#include "mkdir.h"
#endif /* NM_IGNORED */
#include "path-util.h"
#if 0 /* NM_IGNORED */
#include "exit-status.h"
#include "hashmap.h"
#include "env-util.h"
#include "fileio.h"
#include "device-nodes.h"
#endif /* NM_IGNORED */
#include "utf8.h"
#if 0 /* NM_IGNORED */
#include "gunicode.h"
#include "virt.h"
#include "def.h"
#include "sparse-endian.h"
#include "formats-util.h"
#include "process-util.h"
#include "random-util.h"
#include "terminal-util.h"
#include "hostname-util.h"
#include "signal-util.h"
#include "sparse-endian.h"
#include "strv.h"
#include "terminal-util.h"
#endif /* NM_IGNORED */
#include "utf8.h"
#include "util.h"
#if 0 /* NM_IGNORED */
#include "virt.h"
#endif /* NM_IGNORED */
/* Put this test here for a lack of better place */
@ -109,6 +114,7 @@ assert_cc(EAGAIN == EWOULDBLOCK);
#if 0 /* NM_IGNORED */
int saved_argc = 0;
char **saved_argv = NULL;
#endif /* NM_IGNORED */
size_t page_size(void) {
static thread_local size_t pgsz = 0;
@ -123,7 +129,6 @@ size_t page_size(void) {
pgsz = (size_t) r;
return pgsz;
}
#endif /* NM_IGNORED */
int strcmp_ptr(const char *a, const char *b) {
@ -366,6 +371,17 @@ FILE* safe_fclose(FILE *f) {
return NULL;
}
DIR* safe_closedir(DIR *d) {
if (d) {
PROTECT_ERRNO;
assert_se(closedir(d) >= 0 || errno != EBADF);
}
return NULL;
}
int unlink_noerrno(const char *path) {
PROTECT_ERRNO;
int r;
@ -2159,7 +2175,13 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
assert(buf);
while (nbytes > 0) {
/* If called with nbytes == 0, let's call read() at least
* once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
do {
ssize_t k;
k = read(fd, p, nbytes);
@ -2173,7 +2195,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
* and expect that any error/EOF is reported
* via read() */
fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
(void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
continue;
}
@ -2183,10 +2205,12 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
if (k == 0)
return n;
assert((size_t) k <= nbytes);
p += k;
nbytes -= k;
n += k;
}
} while (nbytes > 0);
return n;
}
@ -2196,9 +2220,10 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
n = loop_read(fd, buf, nbytes, do_poll);
if (n < 0)
return n;
return (int) n;
if ((size_t) n != nbytes)
return -EIO;
return 0;
}
@ -2209,7 +2234,8 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
assert(buf);
errno = 0;
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
do {
ssize_t k;
@ -2224,16 +2250,18 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
* and expect that any error/EOF is reported
* via write() */
fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
(void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
continue;
}
return -errno;
}
if (nbytes > 0 && k == 0) /* Can't really happen */
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
return -EIO;
assert((size_t) k <= nbytes);
p += k;
nbytes -= k;
} while (nbytes > 0);
@ -2552,34 +2580,6 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
return 0;
}
cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
cpu_set_t *r;
unsigned n = 1024;
/* Allocates the cpuset in the right size */
for (;;) {
if (!(r = CPU_ALLOC(n)))
return NULL;
if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
if (ncpus)
*ncpus = n;
return r;
}
CPU_FREE(r);
if (errno != EINVAL)
return NULL;
n *= 2;
}
}
int files_same(const char *filea, const char *fileb) {
struct stat a, b;
@ -5279,6 +5279,19 @@ unsigned long personality_from_string(const char *p) {
if (streq(p, "x86"))
return PER_LINUX;
#elif defined(__s390x__)
if (streq(p, "s390"))
return PER_LINUX32;
if (streq(p, "s390x"))
return PER_LINUX;
#elif defined(__s390__)
if (streq(p, "s390"))
return PER_LINUX;
#endif
return PERSONALITY_INVALID;
@ -5298,6 +5311,20 @@ const char* personality_to_string(unsigned long p) {
if (p == PER_LINUX)
return "x86";
#elif defined(__s390x__)
if (p == PER_LINUX)
return "s390x";
if (p == PER_LINUX32)
return "s390";
#elif defined(__s390__)
if (p == PER_LINUX)
return "s390";
#endif
return NULL;
@ -5362,15 +5389,13 @@ int update_reboot_param_file(const char *param) {
int r = 0;
if (param) {
r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
if (r < 0)
log_error("Failed to write reboot param to "
REBOOT_PARAM_FILE": %s", strerror(-r));
return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
} else
unlink(REBOOT_PARAM_FILE);
(void) unlink(REBOOT_PARAM_FILE);
return r;
return 0;
}
int umount_recursive(const char *prefix, int flags) {
@ -6006,6 +6031,7 @@ int extract_first_word_and_warn(
const char *filename,
unsigned line,
const char *rvalue) {
/* Try to unquote it, if it fails, warn about it and try again but this
* time using EXTRACT_CUNESCAPE_RELAX to keep the backslashes verbatim
* in invalid escape sequences. */
@ -6014,17 +6040,17 @@ int extract_first_word_and_warn(
save = *p;
r = extract_first_word(p, ret, separators, flags);
if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) {
if (r < 0 && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
/* Retry it with EXTRACT_CUNESCAPE_RELAX. */
*p = save;
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
else
log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
"Invalid escape sequences in command line: \"%s\"", rvalue);
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid escape sequences in command line: \"%s\"", rvalue);
}
return r;
}
@ -6138,15 +6164,6 @@ int ptsname_malloc(int fd, char **ret) {
int openpt_in_namespace(pid_t pid, int flags) {
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
siginfo_t si;
pid_t child;
int r;
@ -6180,15 +6197,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (unlockpt(master) < 0)
_exit(EXIT_FAILURE);
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
mh.msg_controllen = cmsg->cmsg_len;
if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
if (send_one_fd(pair[1], master, 0) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
@ -6202,39 +6211,23 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
return -EIO;
if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
return -errno;
CMSG_FOREACH(cmsg, &mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int *fds;
unsigned n_fds;
fds = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
if (n_fds != 1) {
close_many(fds, n_fds);
return -EIO;
}
return fds[0];
}
return -EIO;
return receive_one_fd(pair[0], 0);
}
ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1;
ssize_t l;
/* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
if (fd < 0)
return -errno;
l = fgetxattr(fd, attribute, value, size);
xsprintf(fn, "/proc/self/fd/%i", fd);
l = getxattr(fn, attribute, value, size);
if (l < 0)
return -errno;
@ -6584,7 +6577,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
for (i = 0; i < len; ++i)
if (streq_ptr(table[i], key))
return (ssize_t)i;
return (ssize_t) i;
return -1;
}
@ -6824,4 +6817,110 @@ int fgetxattr_malloc(int fd, const char *name, char **value) {
return -errno;
}
}
int send_one_fd(int transport_fd, int fd, int flags) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
assert(transport_fd >= 0);
assert(fd >= 0);
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
mh.msg_controllen = CMSG_SPACE(sizeof(int));
if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
return -errno;
return 0;
}
int receive_one_fd(int transport_fd, int flags) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg, *found = NULL;
assert(transport_fd >= 0);
/*
* Receive a single FD via @transport_fd. We don't care for
* the transport-type. We retrieve a single FD at most, so for
* packet-based transports, the caller must ensure to send
* only a single FD per packet. This is best used in
* combination with send_one_fd().
*/
if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
return -errno;
CMSG_FOREACH(cmsg, &mh) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
assert(!found);
found = cmsg;
break;
}
}
if (!found) {
cmsg_close_all(&mh);
return -EIO;
}
return *(int*) CMSG_DATA(found);
}
void nop_signal_handler(int sig) {
/* nothing here */
}
int version(void) {
puts(PACKAGE_STRING "\n"
SYSTEMD_FEATURES);
return 0;
}
bool fdname_is_valid(const char *s) {
const char *p;
/* Validates a name for $LISTEN_FDNAMES. We basically allow
* everything ASCII that's not a control character. Also, as
* special exception the ":" character is not allowed, as we
* use that as field separator in $LISTEN_FDNAMES.
*
* Note that the empty string is explicitly allowed
* here. However, we limit the length of the names to 255
* characters. */
if (!s)
return false;
for (p = s; *p; p++) {
if (*p < ' ')
return false;
if (*p >= 127)
return false;
if (*p == ':')
return false;
}
return p - s < 256;
}
#endif /* NM_IGNORED */

View file

@ -24,34 +24,33 @@
#include "nm-sd-adapt.h"
#include <alloca.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
#include <time.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <sched.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stddef.h>
#include <unistd.h>
#include <locale.h>
#include <mntent.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#if 0 /* NM_IGNORED */
#include "formats-util.h"
#endif /* NM_IGNORED */
#include "macro.h"
#if 0 /* NM_IGNORED */
#include "missing.h"
#endif /* NM_IGNORED */
#include "time-util.h"
#if 0 /* NM_IGNORED */
#include "formats-util.h"
#endif /* NM_IGNORED */
/* What is interpreted as whitespace? */
#define WHITESPACE " \t\n\r"
@ -157,6 +156,7 @@ void close_many(const int fds[], unsigned n_fd);
int fclose_nointr(FILE *f);
FILE* safe_fclose(FILE *f);
DIR* safe_closedir(DIR *f);
int parse_size(const char *t, uint64_t base, uint64_t *size);
@ -166,7 +166,10 @@ int parse_uid(const char *s, uid_t* ret_uid);
#define parse_gid(s, ret_gid) parse_uid(s, ret_gid)
bool uid_is_valid(uid_t uid);
#define gid_is_valid(gid) uid_is_valid(gid)
static inline bool gid_is_valid(gid_t gid) {
return uid_is_valid((uid_t) gid);
}
int safe_atou(const char *s, unsigned *ret_u);
int safe_atoi(const char *s, int *ret_i);
@ -295,9 +298,9 @@ bool chars_intersect(const char *a, const char *b) _pure_;
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
scope inline type name##_from_string(const char *s) { \
return (type)string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
scope type name##_from_string(const char *s) { \
return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
}
#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
@ -314,17 +317,15 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
int name##_to_string_alloc(type i, char **str) { \
char *s; \
int r; \
if (i < 0 || i > max) \
return -ERANGE; \
if (i < (type) ELEMENTSOF(name##_table)) { \
s = strdup(name##_table[i]); \
if (!s) \
return log_oom(); \
return -ENOMEM; \
} else { \
r = asprintf(&s, "%i", i); \
if (r < 0) \
return log_oom(); \
if (asprintf(&s, "%i", i) < 0) \
return -ENOMEM; \
} \
*str = s; \
return 0; \
@ -332,10 +333,10 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
type name##_from_string(const char *s) { \
type i; \
unsigned u = 0; \
assert(s); \
for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
if (name##_table[i] && \
streq(name##_table[i], s)) \
if (!s) \
return (type) -1; \
for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \
if (streq_ptr(name##_table[i], s)) \
return i; \
if (safe_atou(s, &u) >= 0 && u <= max) \
return (type) u; \
@ -375,12 +376,9 @@ int fd_is_temporary_fs(int fd);
int pipe_eof(int fd);
DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
cpu_set_t* cpu_set_malloc(unsigned *ncpus);
#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
#define xsprintf(buf, fmt, ...) \
assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), \
"xsprintf: " #buf "[] must be big enough")
int files_same(const char *filea, const char *fileb);
@ -440,7 +438,9 @@ int get_files_in_directory(const char *path, char ***list);
char *strjoin(const char *x, ...) _sentinel_;
#if 0 /* NM_IGNORED */
bool is_main_thread(void);
#endif /* NM_IGNORED */
static inline bool _pure_ in_charset(const char *s, const char* charset) {
assert(s);
@ -948,3 +948,12 @@ int reset_uid_gid(void);
int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink);
int fgetxattr_malloc(int fd, const char *name, char **value);
int send_one_fd(int transport_fd, int fd, int flags);
int receive_one_fd(int transport_fd, int flags);
void nop_signal_handler(int sig);
int version(void);
bool fdname_is_valid(const char *s);

View file

@ -0,0 +1,155 @@
/***
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
Copyright (C) 2015 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <linux/filter.h>
#include <arpa/inet.h>
#include "util.h"
#include "arp-util.h"
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Hardware Address must be different from our own */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */
BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/
BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
};
struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
.filter = (struct sock_filter*) filter
};
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htons(ETH_P_ARP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
_cleanup_close_ int s = -1;
int r;
assert(ifindex > 0);
s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (s < 0)
return -errno;
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0)
return -errno;
r = bind(s, &link.sa, sizeof(link.ll));
if (r < 0)
return -errno;
r = s;
s = -1;
return r;
}
static int arp_send_packet(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha,
bool announce) {
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htons(ETH_P_ARP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
struct ether_arp arp = {
.ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */
.ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */
.ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
.ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
.ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */
};
int r;
assert(fd >= 0);
assert(pa != 0);
assert(ha);
memcpy(&arp.arp_sha, ha, ETH_ALEN);
memcpy(&arp.arp_tpa, &pa, sizeof(pa));
if (announce)
memcpy(&arp.arp_spa, &pa, sizeof(pa));
r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
if (r < 0)
return -errno;
return 0;
}
int arp_send_probe(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, false);
}
int arp_send_announcement(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, true);
}

View file

@ -28,13 +28,9 @@
#include "sparse-endian.h"
#include "socket-util.h"
int arp_network_bind_raw_socket(int index, union sockaddr_union *link);
int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
const struct ether_arp *arp);
int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
void arp_packet_init(struct ether_arp *arp);
void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
int arp_packet_verify_headers(struct ether_arp *arp);
#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__)
int arp_send_probe(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha);
int arp_send_announcement(int fd, int ifindex,
be32_t pa, const struct ether_addr *ha);

View file

@ -1,93 +0,0 @@
/***
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <linux/filter.h>
#include "util.h"
#include "ipv4ll-internal.h"
int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
const struct ether_arp *arp) {
int r;
assert(arp);
assert(link);
assert(fd >= 0);
r = sendto(fd, arp, sizeof(struct ether_arp), 0, &link->sa, sizeof(link->ll));
if (r < 0)
return -errno;
return 0;
}
int arp_network_bind_raw_socket(int ifindex, union sockaddr_union *link) {
static const struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 0, 1), /* protocol == request ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), /* protocol == reply ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
};
struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
.filter = (struct sock_filter*) filter
};
_cleanup_close_ int s = -1;
int r;
assert(ifindex > 0);
assert(link);
s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (s < 0)
return -errno;
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0)
return -errno;
link->ll.sll_family = AF_PACKET;
link->ll.sll_protocol = htons(ETH_P_ARP);
link->ll.sll_ifindex = ifindex;
link->ll.sll_halen = ETH_ALEN;
memset(link->ll.sll_addr, 0xff, ETH_ALEN);
r = bind(s, &link->sa, sizeof(link->ll));
if (r < 0)
return -errno;
r = s;
s = -1;
return r;
}

View file

@ -1,74 +0,0 @@
/***
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <arpa/inet.h>
#include "util.h"
#include "ipv4ll-internal.h"
void arp_packet_init(struct ether_arp *arp) {
assert(arp);
memzero(arp, sizeof(struct ether_arp));
/* Header */
arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); /* HTYPE */
arp->ea_hdr.ar_pro = htons(ETHERTYPE_IP); /* PTYPE */
arp->ea_hdr.ar_hln = ETH_ALEN; /* HLEN */
arp->ea_hdr.ar_pln = sizeof arp->arp_spa; /* PLEN */
arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); /* REQUEST */
}
void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
assert(ha);
arp_packet_init(arp);
memcpy(arp->arp_sha, ha, ETH_ALEN);
memcpy(arp->arp_tpa, &pa, sizeof(pa));
}
void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
assert(ha);
arp_packet_init(arp);
memcpy(arp->arp_sha, ha, ETH_ALEN);
memcpy(arp->arp_tpa, &pa, sizeof(pa));
memcpy(arp->arp_spa, &pa, sizeof(pa));
}
int arp_packet_verify_headers(struct ether_arp *arp) {
assert(arp);
if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) {
log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER");
return -EINVAL;
}
if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) {
log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP");
return -EINVAL;
}
if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) &&
arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) {
log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY");
return -EINVAL;
}
return 0;
}

View file

@ -0,0 +1,362 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "lldp-internal.h"
#include "sd-lldp.h"
/* We store maximum 1K chassis entries */
#define LLDP_MIB_MAX_CHASSIS 1024
/* Maximum Ports can be attached to any chassis */
#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
/* 10.5.5.2.2 mibUpdateObjects ()
* The mibUpdateObjects () procedure updates the MIB objects corresponding to
* the TLVs contained in the received LLDPDU for the LLDP remote system
* indicated by the LLDP remote systems update process defined in 10.3.5 */
int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
lldp_neighbour_port *p;
uint16_t length, ttl;
uint8_t *data;
uint8_t type;
int r;
assert_return(c, -EINVAL);
assert_return(tlv, -EINVAL);
r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
/* Update the packet if we already have */
LIST_FOREACH(port, p, c->ports) {
if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
return r;
p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
sd_lldp_packet_unref(p->packet);
p->packet = tlv;
prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
return 0;
}
}
return -1;
}
int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
lldp_neighbour_port *p, *q;
uint8_t *data;
uint16_t length;
uint8_t type;
int r;
assert_return(c, -EINVAL);
assert_return(tlv, -EINVAL);
r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
LIST_FOREACH_SAFE(port, p, q, c->ports) {
/* Find the port */
if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
lldp_neighbour_port_remove_and_free(p);
break;
}
}
return 0;
}
int lldp_mib_add_objects(Prioq *by_expiry,
Hashmap *neighbour_mib,
tlv_packet *tlv) {
_cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
_cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
lldp_chassis_id chassis_id;
bool new_chassis = false;
uint8_t subtype, *data;
uint16_t ttl, length;
int r;
assert_return(by_expiry, -EINVAL);
assert_return(neighbour_mib, -EINVAL);
assert_return(tlv, -EINVAL);
r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
if (r < 0)
goto drop;
r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
goto drop;
/* Make hash key */
chassis_id.type = subtype;
chassis_id.length = length;
chassis_id.data = data;
/* Try to find the Chassis */
c = hashmap_get(neighbour_mib, &chassis_id);
if (!c) {
/* Don't create chassis if ttl 0 is received . Silently drop it */
if (ttl == 0) {
log_lldp("TTL value 0 received. Skiping Chassis creation.");
goto drop;
}
/* Admission Control: Can we store this packet ? */
if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
log_lldp("Exceeding number of chassie: %d. Dropping ...",
hashmap_size(neighbour_mib));
goto drop;
}
r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
if (r < 0)
goto drop;
new_chassis = true;
r = hashmap_put(neighbour_mib, &c->chassis_id, c);
if (r < 0)
goto drop;
} else {
/* When the TTL field is set to zero, the receiving LLDP agent is notified all
* system information associated with the LLDP agent/port is to be deleted */
if (ttl == 0) {
log_lldp("TTL value 0 received . Deleting associated Port ...");
lldp_mib_remove_objects(c, tlv);
c = NULL;
goto drop;
}
/* if we already have this port just update it */
r = lldp_mib_update_objects(c, tlv);
if (r >= 0) {
c = NULL;
return r;
}
/* Admission Control: Can this port attached to the existing chassis ? */
if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
c = NULL;
goto drop;
}
}
/* This is a new port */
r = lldp_neighbour_port_new(c, tlv, &p);
if (r < 0)
goto drop;
r = prioq_put(c->by_expiry, p, &p->prioq_idx);
if (r < 0)
goto drop;
/* Attach new port to chassis */
LIST_PREPEND(port, c->ports, p);
c->n_ref ++;
p = NULL;
c = NULL;
return 0;
drop:
sd_lldp_packet_unref(tlv);
if (new_chassis)
hashmap_remove(neighbour_mib, &c->chassis_id);
return r;
}
void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
lldp_chassis *c;
assert(p);
assert(p->c);
c = p->c;
prioq_remove(c->by_expiry, p, &p->prioq_idx);
LIST_REMOVE(port, c->ports, p);
lldp_neighbour_port_free(p);
/* Drop the Chassis if no port is attached */
c->n_ref --;
if (c->n_ref <= 1) {
hashmap_remove(c->neighbour_mib, &c->chassis_id);
lldp_chassis_free(c);
}
}
void lldp_neighbour_port_free(lldp_neighbour_port *p) {
if(!p)
return;
sd_lldp_packet_unref(p->packet);
free(p->data);
free(p);
}
int lldp_neighbour_port_new(lldp_chassis *c,
tlv_packet *tlv,
lldp_neighbour_port **ret) {
_cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
uint16_t length, ttl;
uint8_t *data;
uint8_t type;
int r;
assert(tlv);
r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
return r;
p = new0(lldp_neighbour_port, 1);
if (!p)
return -ENOMEM;
p->c = c;
p->type = type;
p->length = length;
p->packet = tlv;
p->prioq_idx = PRIOQ_IDX_NULL;
p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
p->data = memdup(data, length);
if (!p->data)
return -ENOMEM;
*ret = p;
p = NULL;
return 0;
}
void lldp_chassis_free(lldp_chassis *c) {
if (!c)
return;
if (c->n_ref > 1)
return;
free(c->chassis_id.data);
free(c);
}
int lldp_chassis_new(tlv_packet *tlv,
Prioq *by_expiry,
Hashmap *neighbour_mib,
lldp_chassis **ret) {
_cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
uint16_t length;
uint8_t *data;
uint8_t type;
int r;
assert(tlv);
r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
if (r < 0)
return r;
c = new0(lldp_chassis, 1);
if (!c)
return -ENOMEM;
c->n_ref = 1;
c->chassis_id.type = type;
c->chassis_id.length = length;
c->chassis_id.data = memdup(data, length);
if (!c->chassis_id.data)
return -ENOMEM;
LIST_HEAD_INIT(c->ports);
c->by_expiry = by_expiry;
c->neighbour_mib = neighbour_mib;
*ret = c;
c = NULL;
return 0;
}
int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_lldp_packet_unref_ tlv_packet *packet = NULL;
tlv_packet *p;
uint16_t length;
int r;
assert(fd);
assert(userdata);
r = tlv_packet_new(&packet);
if (r < 0)
return r;
length = read(fd, &packet->pdu, sizeof(packet->pdu));
/* Silently drop the packet */
if ((size_t) length > ETHER_MAX_LEN)
return 0;
packet->userdata = userdata;
p = packet;
packet = NULL;
return lldp_handle_packet(p, (uint16_t) length);
}

View file

@ -0,0 +1,94 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "nm-sd-adapt.h"
#include "log.h"
#include "list.h"
#include "lldp-tlv.h"
#include "prioq.h"
#include "sd-event.h"
typedef struct lldp_neighbour_port lldp_neighbour_port;
typedef struct lldp_chassis lldp_chassis;
typedef struct lldp_chassis_id lldp_chassis_id;
typedef struct lldp_agent_statistics lldp_agent_statistics;
struct lldp_neighbour_port {
uint8_t type;
uint8_t *data;
uint16_t length;
usec_t until;
unsigned prioq_idx;
lldp_chassis *c;
tlv_packet *packet;
LIST_FIELDS(lldp_neighbour_port, port);
};
int lldp_neighbour_port_new(lldp_chassis *c, tlv_packet *tlv, lldp_neighbour_port **ret);
void lldp_neighbour_port_free(lldp_neighbour_port *p);
void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_neighbour_port *, lldp_neighbour_port_free);
#define _cleanup_lldp_neighbour_port_free_ _cleanup_(lldp_neighbour_port_freep)
struct lldp_chassis_id {
uint8_t type;
uint16_t length;
uint8_t *data;
};
struct lldp_chassis {
unsigned n_ref;
lldp_chassis_id chassis_id;
Prioq *by_expiry;
Hashmap *neighbour_mib;
LIST_HEAD(lldp_neighbour_port, ports);
};
int lldp_chassis_new(tlv_packet *tlv,
Prioq *by_expiry,
Hashmap *neighbour_mib,
lldp_chassis **ret);
void lldp_chassis_free(lldp_chassis *c);
DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_chassis *, lldp_chassis_free);
#define _cleanup_lldp_chassis_free_ _cleanup_(lldp_chassis_freep)
int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
int lldp_handle_packet(tlv_packet *m, uint16_t length);
int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)

View file

@ -0,0 +1,86 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <linux/filter.h>
#include <linux/if_ether.h>
#include "socket-util.h"
#include "lldp-tlv.h"
#include "lldp-network.h"
#include "lldp-internal.h"
int lldp_network_bind_raw_socket(int ifindex) {
typedef struct LLDPFrame {
struct ethhdr hdr;
uint8_t tlvs[0];
} LLDPFrame;
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest)), /* A <- 4 bytes of destination MAC */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_proto)), /* A <- protocol */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */
};
struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
.filter = filter
};
_cleanup_close_ int s = -1;
union sockaddr_union saddrll = {
.ll.sll_family = AF_PACKET,
.ll.sll_ifindex = ifindex,
};
int r;
assert(ifindex > 0);
s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s < 0)
return -errno;
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0)
return -errno;
r = bind(s, &saddrll.sa, sizeof(saddrll.ll));
if (r < 0)
return -errno;
r = s;
s = -1;
return r;
}

View file

@ -0,0 +1,29 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "nm-sd-adapt.h"
#include "sd-event.h"
int lldp_network_bind_raw_socket(int ifindex);

View file

@ -0,0 +1,119 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "async.h"
#include "lldp-port.h"
#include "lldp-network.h"
#include "lldp-internal.h"
int lldp_port_start(lldp_port *p) {
int r;
assert_return(p, -EINVAL);
r = lldp_network_bind_raw_socket(p->ifindex);
if (r < 0)
return r;
p->rawfd = r;
r = sd_event_add_io(p->event, &p->lldp_port_rx,
p->rawfd, EPOLLIN, lldp_receive_packet, p);
if (r < 0) {
log_debug_errno(r, "Failed to allocate event source: %m");
goto fail;
}
r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority);
if (r < 0) {
log_debug_errno(r, "Failed to set event priority: %m");
goto fail;
}
r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx");
if (r < 0) {
log_debug_errno(r, "Failed to set event name: %m");
goto fail;
}
return 0;
fail:
lldp_port_stop(p);
return r;
}
int lldp_port_stop(lldp_port *p) {
assert_return(p, -EINVAL);
p->rawfd = asynchronous_close(p->rawfd);
p->lldp_port_rx = sd_event_source_unref(p->lldp_port_rx);
return 0;
}
void lldp_port_free(lldp_port *p) {
if (!p)
return;
lldp_port_stop(p);
free(p->ifname);
free(p);
}
int lldp_port_new(int ifindex,
const char *ifname,
const struct ether_addr *addr,
void *userdata,
lldp_port **ret) {
_cleanup_free_ lldp_port *p = NULL;
assert_return(ifindex, -EINVAL);
assert_return(ifname, -EINVAL);
assert_return(addr, -EINVAL);
p = new0(lldp_port, 1);
if (!p)
return -ENOMEM;
p->rawfd = -1;
p->ifindex = ifindex;
p->ifname = strdup(ifname);
if (!p->ifname)
return -ENOMEM;
memcpy(&p->mac, addr, ETH_ALEN);
p->userdata = userdata;
*ret = p;
p = NULL;
return 0;
}

View file

@ -0,0 +1,73 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "nm-sd-adapt.h"
#include <net/ethernet.h>
#include "sd-event.h"
#include "sd-lldp.h"
#include "util.h"
typedef struct lldp_port lldp_port;
typedef enum LLDPPortStatus {
LLDP_PORT_STATUS_NONE,
LLDP_PORT_STATUS_ENABLED,
LLDP_PORT_STATUS_DISABLED,
_LLDP_PORT_STATUS_MAX,
_LLDP_PORT_STATUS_INVALID = -1,
} LLDPPortStatus;
struct lldp_port {
LLDPPortStatus status;
int ifindex;
char *ifname;
struct ether_addr mac;
int rawfd;
sd_event *event;
sd_event_source *lldp_port_rx;
int event_priority;
void *userdata;
};
int lldp_port_new(int ifindex,
const char *ifname,
const struct ether_addr *addr,
void *userdata,
lldp_port **ret);
void lldp_port_free(lldp_port *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_port*, lldp_port_free);
#define _cleanup_lldp_port_free_ _cleanup_(lldp_port_freep)
int lldp_port_start(lldp_port *p);
int lldp_port_stop(lldp_port *p);

View file

@ -0,0 +1,649 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <net/ethernet.h>
#include <arpa/inet.h>
#include "macro.h"
#include "lldp-tlv.h"
int tlv_section_new(tlv_section **ret) {
tlv_section *s;
s = new0(tlv_section, 1);
if (!s)
return -ENOMEM;
*ret = s;
return 0;
}
void tlv_section_free(tlv_section *m) {
if (!m)
return;
free(m);
}
int tlv_packet_new(tlv_packet **ret) {
tlv_packet *m;
m = new0(tlv_packet, 1);
if (!m)
return -ENOMEM;
LIST_HEAD_INIT(m->sections);
m->n_ref = 1;
*ret = m;
return 0;
}
tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
if (!m)
return NULL;
assert(m->n_ref > 0);
m->n_ref++;
return m;
}
tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
tlv_section *s, *n;
if (!m)
return NULL;
assert(m->n_ref > 0);
m->n_ref--;
if (m->n_ref > 0)
return m;
LIST_FOREACH_SAFE(section, s, n, m->sections)
tlv_section_free(s);
free(m);
return NULL;
}
int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
uint8_t *p;
assert_return(m, -EINVAL);
assert_return(data, -EINVAL);
assert_return(data_length, -EINVAL);
if (m->length + data_length > ETHER_MAX_LEN)
return -ENOMEM;
p = m->pdu + m->length;
memcpy(p, data, data_length);
m->length += data_length;
return 0;
}
int tlv_packet_append_u8(tlv_packet *m, uint8_t data) {
assert_return(m, -EINVAL);
return tlv_packet_append_bytes(m, &data, sizeof(uint8_t));
}
int tlv_packet_append_u16(tlv_packet *m, uint16_t data) {
uint16_t type;
assert_return(m, -EINVAL);
type = htons(data);
return tlv_packet_append_bytes(m, &type, sizeof(uint16_t));
}
int tlv_packet_append_u32(tlv_packet *m, uint32_t data) {
uint32_t type;
assert_return(m, -EINVAL);
type = htonl(data);
return tlv_packet_append_bytes(m, &type, sizeof(uint32_t));
}
int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) {
assert_return(m, -EINVAL);
return tlv_packet_append_bytes(m, data, size);
}
int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) {
assert_return(m, -EINVAL);
m->container_pos = m->pdu + m->length;
return tlv_packet_append_u16(m, type << 9);
}
int lldp_tlv_packet_close_container(tlv_packet *m) {
uint16_t type;
assert_return(m, -EINVAL);
assert_return(m->container_pos, -EINVAL);
memcpy(&type, m->container_pos, sizeof(uint16_t));
type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff);
memcpy(m->container_pos, &type, sizeof(uint16_t));
return 0;
}
static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
assert_return(m->read_pos, -EINVAL);
*data = m->read_pos;
return 0;
}
int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
memcpy(data, val, sizeof(uint8_t));
m->container->read_pos ++;
return 0;
}
int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
uint16_t t;
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
memcpy(&t, val, sizeof(uint16_t));
*data = ntohs(t);
m->container->read_pos += 2;
return 0;
}
int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
uint32_t t;
void *val;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
memcpy(&t, val, sizeof(uint32_t));
*data = ntohl(t);
m->container->read_pos += 4;
return r;
}
int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
*data = (char *) val;
*data_length = m->container->data + m->container->length - m->container->read_pos;
m->container->read_pos += *data_length;
return 0;
}
int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
*data = (uint8_t *) val;
*data_length = m->container->data + m->container->length - m->container->read_pos;
m->container->read_pos += *data_length;
return 0;
}
/* parse raw TLV packet */
int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
tlv_section *section, *tail;
uint16_t t, l;
uint8_t *p;
int r;
assert_return(m, -EINVAL);
assert_return(size, -EINVAL);
p = m->pdu;
/* extract ethernet header */
memcpy(&m->mac, p, ETH_ALEN);
p += sizeof(struct ether_header);
for (l = 0; l <= size; ) {
r = tlv_section_new(&section);
if (r < 0)
return r;
memcpy(&t, p, sizeof(uint16_t));
section->type = ntohs(t) >> 9;
section->length = ntohs(t) & 0x01ff;
if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) {
tlv_section_free(section);
break;
}
p += 2;
if (section->type == LLDP_TYPE_PRIVATE &&
section->length >= LLDP_OUI_LEN + 1) {
section->oui = p;
p += LLDP_OUI_LEN;
section->subtype = *p++;
section->length -= LLDP_OUI_LEN + 1;
l += LLDP_OUI_LEN + 1;
}
section->data = p;
LIST_FIND_TAIL(section, m->sections, tail);
LIST_INSERT_AFTER(section, m->sections, tail, section);
p += section->length;
l += (section->length + 2);
}
return 0;
}
int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
tlv_section *s;
assert_return(m, -EINVAL);
assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
LIST_FOREACH(section, s, m->sections)
if (s->type == type)
break;
if (!s)
return -1;
m->container = s;
m->container->read_pos = s->data;
if (!m->container->read_pos) {
m->container = NULL;
return -1;
}
return 0;
}
int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
tlv_section *s;
assert_return(m, -EINVAL);
assert_return(oui, -EINVAL);
LIST_FOREACH(section, s, m->sections) {
if (s->type == LLDP_TYPE_PRIVATE &&
s->oui &&
s->subtype == subtype &&
!memcmp(s->oui, oui, LLDP_OUI_LEN))
break;
}
if (!s)
return -1;
m->container = s;
m->container->read_pos = s->data;
if (!m->container->read_pos) {
m->container = NULL;
return -1;
}
return 0;
}
int lldp_tlv_packet_exit_container(tlv_packet *m) {
assert_return(m, -EINVAL);
m->container = 0;
return 0;
}
static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, type);
if (r < 0)
goto out;
r = tlv_packet_read_u16(tlv, value);
r2 = lldp_tlv_packet_exit_container(tlv);
out:
return r < 0 ? r : r2;
}
static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
char *s;
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, type);
if (r < 0)
return r;
r = tlv_packet_read_string(tlv, &s, length);
if (r < 0)
goto out;
*data = (char *) s;
out:
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
uint8_t *type,
uint8_t **data,
uint16_t *length) {
uint8_t subtype;
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
if (r < 0)
goto out2;
r = tlv_packet_read_u8(tlv, &subtype);
if (r < 0)
goto out1;
switch (subtype) {
case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
r = tlv_packet_read_bytes(tlv, data, length);
if (r < 0)
goto out1;
break;
default:
r = -EOPNOTSUPP;
break;
}
*type = subtype;
out1:
r2 = lldp_tlv_packet_exit_container(tlv);
out2:
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_port_id(tlv_packet *tlv,
uint8_t *type,
uint8_t **data,
uint16_t *length) {
uint8_t subtype;
char *s;
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
if (r < 0)
goto out2;
r = tlv_packet_read_u8(tlv, &subtype);
if (r < 0)
goto out1;
switch (subtype) {
case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
r = tlv_packet_read_string(tlv, &s, length);
if (r < 0)
goto out1;
*data = (uint8_t *) s;
break;
case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
r = tlv_packet_read_bytes(tlv, data, length);
if (r < 0)
goto out1;
break;
default:
r = -EOPNOTSUPP;
break;
}
*type = subtype;
out1:
r2 = lldp_tlv_packet_exit_container(tlv);
out2:
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
}
int sd_lldp_packet_read_system_name(tlv_packet *tlv,
char **data,
uint16_t *length) {
return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
}
int sd_lldp_packet_read_system_description(tlv_packet *tlv,
char **data,
uint16_t *length) {
return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
}
int sd_lldp_packet_read_port_description(tlv_packet *tlv,
char **data,
uint16_t *length) {
return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
}
int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
}
int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
if (r < 0)
goto out;
r = tlv_packet_read_u16(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
out:
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
if (r < 0)
goto out;
r = tlv_packet_read_u8(tlv, flags);
if (r >= 0)
r = tlv_packet_read_u16(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
out:
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
int r, r2;
uint8_t len = 0;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
if (r < 0)
goto out;
r = tlv_packet_read_u16(tlv, vlan_id);
if (r >= 0)
r = tlv_packet_read_u8(tlv, &len);
if (r >= 0)
r = tlv_packet_read_string(tlv, name, length);
if (r >= 0 && len < *length)
*length = len;
r2 = lldp_tlv_packet_exit_container(tlv);
out:
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
if (r < 0)
goto out;
r = tlv_packet_read_u16(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
out:
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
if (r < 0)
goto out;
r = tlv_packet_read_u8(tlv, status);
if (r >= 0)
r = tlv_packet_read_u32(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
out:
return r < 0 ? r : r2;
}
int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
assert_return(tlv, -EINVAL);
assert_return(dest, -EINVAL);
/* 802.1AB-2009, Table 7-1 */
if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
*dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
*dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
*dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
else
return -EINVAL;
return 0;
}

View file

@ -0,0 +1,101 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "nm-sd-adapt.h"
#include <net/ethernet.h>
#include "util.h"
#include "lldp.h"
#include "list.h"
#include "sd-lldp.h"
typedef struct tlv_packet tlv_packet;
typedef struct tlv_section tlv_section;
#define LLDP_OUI_LEN 3
struct tlv_section {
uint16_t type;
uint16_t length;
uint8_t *oui;
uint8_t subtype;
uint8_t *read_pos;
uint8_t *data;
LIST_FIELDS(tlv_section, section);
};
#define LLDP_MAC_NEAREST_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }
int tlv_section_new(tlv_section **ret);
void tlv_section_free(tlv_section *ret);
struct tlv_packet {
unsigned n_ref;
uint16_t type;
uint16_t length;
usec_t ts;
uint8_t *container_pos;
uint8_t pdu[ETHER_MAX_LEN];
void *userdata;
struct ether_addr mac;
tlv_section *container;
LIST_HEAD(tlv_section, sections);
};
int tlv_packet_new(tlv_packet **ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp_packet*, sd_lldp_packet_unref);
#define _cleanup_lldp_packet_unref_ _cleanup_(sd_lldp_packet_unrefp)
int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
int lldp_tlv_packet_close_container(tlv_packet *m);
int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length);
int tlv_packet_append_u8(tlv_packet *m, uint8_t data);
int tlv_packet_append_u16(tlv_packet *m, uint16_t data);
int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype);
int lldp_tlv_packet_exit_container(tlv_packet *m);
int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length);
int tlv_packet_read_u8(tlv_packet *m, uint8_t *data);
int tlv_packet_read_u16(tlv_packet *m, uint16_t *data);
int tlv_packet_read_u32(tlv_packet *m, uint32_t *data);
int tlv_packet_parse_pdu(tlv_packet *t, uint16_t size);

View file

@ -0,0 +1,28 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "nm-sd-adapt.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp *, sd_lldp_free);
#define _cleanup_lldp_free_ _cleanup_(sd_lldp_freep)

View file

@ -0,0 +1,130 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "nm-sd-adapt.h"
#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
#define ETHERTYPE_LLDP 0x88cc
/* IEEE 802.3AB Clause 9: TLV Types */
typedef enum LLDPTypes {
LLDP_TYPE_END = 0,
LLDP_TYPE_CHASSIS_ID = 1,
LLDP_TYPE_PORT_ID = 2,
LLDP_TYPE_TTL = 3,
LLDP_TYPE_PORT_DESCRIPTION = 4,
LLDP_TYPE_SYSTEM_NAME = 5,
LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
LLDP_TYPE_MGMT_ADDRESS = 8,
LLDP_TYPE_PRIVATE = 127,
_LLDP_TYPE_MAX,
_LLDP_TYPE_INVALID = -1,
} LLDPTypes;
/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
typedef enum LLDPChassisSubtypes {
LLDP_CHASSIS_SUBTYPE_RESERVED = 0,
LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1,
LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2,
LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3,
LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4,
LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5,
LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6,
LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7,
_LLDP_CHASSIS_SUBTYPE_MAX,
_LLDP_CHASSIS_SUBTYPE_INVALID = -1,
} LLDPChassisSubtypes;
/* IEEE 802.3AB Clause 9.5.3: Port subtype */
typedef enum LLDPPortSubtypes {
LLDP_PORT_SUBTYPE_RESERVED = 0,
LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
LLDP_PORT_SUBTYPE_NETWORK = 4,
LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
_LLDP_PORT_SUBTYPE_MAX,
_LLDP_PORT_SUBTYPE_INVALID = -1
} LLDPPortSubtypes;
typedef enum LLDPSystemCapabilities {
LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
_LLDP_SYSTEM_CAPABILITIES_MAX,
_LLDP_SYSTEM_CAPABILITIES_INVALID = -1,
} LLDPSystemCapabilities;
typedef enum LLDPMedSubtype {
LLDP_MED_SUBTYPE_RESERVED = 0,
LLDP_MED_SUBTYPE_CAPABILITIES = 1,
LLDP_MED_SUBTYPE_NETWORK_POLICY = 2,
LLDP_MED_SUBTYPE_LOCATION_ID = 3,
LLDP_MED_SUBTYPE_EXTENDED_PVMDI = 4,
LLDP_MED_SUBTYPE_INV_HWREV = 5,
LLDP_MED_SUBTYPE_INV_FWREV = 6,
LLDP_MED_SUBTYPE_INV_SWREV = 7,
LLDP_MED_SUBTYPE_INV_SERIAL = 8,
LLDP_MED_SUBTYPE_INV_MANUFACTURER = 9,
LLDP_MED_SUBTYPE_INV_MODELNAME = 10,
LLDP_MED_SUBTYPE_INV_ASSETID = 11,
_LLDP_MED_SUBTYPE_MAX,
_LLDP_MED_SUBTYPE_INVALID = -1,
} LLDPMedSubtype;
typedef enum LLDPMedCapability {
LLDP_MED_CAPABILITY_CAPAPILITIES = 1 << 0,
LLDP_MED_CAPABILITY_NETWORK_POLICY = 1 << 1,
LLDP_MED_CAPABILITY_LOCATION_ID = 1 << 2,
LLDP_MED_CAPABILITY_EXTENDED_PSE = 1 << 3,
LLDP_MED_CAPABILITY_EXTENDED_PD = 1 << 4,
LLDP_MED_CAPABILITY_INVENTORY = 1 << 5,
LLDP_MED_CAPABILITY_MAX,
LLDP_MED_CAPABILITY_INVALID = -1,
} LLDPMedCapability;
#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
enum {
LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID = 1,
LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID = 2,
LLDP_OUI_SUBTYPE_802_1_VLAN_NAME = 3,
LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY = 4,
LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST = 5,
LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID = 6,
LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION = 7,
};

View file

@ -205,8 +205,7 @@ int config_parse_ifname(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
return 0;
}
@ -249,8 +248,7 @@ int config_parse_ifnames(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@ -287,8 +285,7 @@ int config_parse_ifalias(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
return 0;
}
@ -333,8 +330,7 @@ int config_parse_hwaddr(const char *unit,
&n->ether_addr_octet[4],
&n->ether_addr_octet[5]);
if (r != 6) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Not a valid MAC address, ignoring assignment: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@ -404,8 +400,8 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
assert(size);
for (i = 0; i < size; i++)
fprintf(f, SD_ICMP6_ADDRESS_FORMAT_STR"%s",
SD_ICMP6_ADDRESS_FORMAT_VAL(addresses[i]),
fprintf(f, SD_ICMP6_ND_ADDRESS_FORMAT_STR"%s",
SD_ICMP6_ND_ADDRESS_FORMAT_VAL(addresses[i]),
(i < (size - 1)) ? " ": "");
}

View file

@ -215,7 +215,7 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
log_dhcp_client(client, "Changing MAC address on running DHCP "
"client, restarting");
need_restart = true;
client_stop(client, DHCP_EVENT_STOP);
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
}
memcpy(&client->mac_addr, addr, addr_len);
@ -279,7 +279,7 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
log_dhcp_client(client, "Changing client ID on running DHCP "
"client, restarting");
need_restart = true;
client_stop(client, DHCP_EVENT_STOP);
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
}
client->client_id.type = type;
@ -387,7 +387,7 @@ static void client_stop(sd_dhcp_client *client, int error) {
if (error < 0)
log_dhcp_client(client, "STOPPED: %s", strerror(-error));
else if (error == DHCP_EVENT_STOP)
else if (error == SD_DHCP_CLIENT_EVENT_STOP)
log_dhcp_client(client, "STOPPED");
else
log_dhcp_client(client, "STOPPED: Unknown event");
@ -985,7 +985,7 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec,
log_dhcp_client(client, "EXPIRED");
client_notify(client, DHCP_EVENT_EXPIRED);
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
/* lease was lost, start over if not freed or stopped in callback */
if (client->state != DHCP_STATE_STOPPED) {
@ -1145,14 +1145,14 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
}
}
r = DHCP_EVENT_IP_ACQUIRE;
r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
if (client->lease) {
if (client->lease->address != lease->address ||
client->lease->subnet_mask != lease->subnet_mask ||
client->lease->router != lease->router) {
r = DHCP_EVENT_IP_CHANGE;
r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
} else
r = DHCP_EVENT_RENEW;
r = SD_DHCP_CLIENT_EVENT_RENEW;
client->lease = sd_dhcp_lease_unref(client->lease);
}
@ -1384,8 +1384,8 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
if (IN_SET(client->state, DHCP_STATE_REQUESTING,
DHCP_STATE_REBOOTING))
notify_event = DHCP_EVENT_IP_ACQUIRE;
else if (r != DHCP_EVENT_IP_ACQUIRE)
notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
notify_event = r;
client->state = DHCP_STATE_BOUND;
@ -1635,7 +1635,7 @@ int sd_dhcp_client_stop(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
client_stop(client, DHCP_EVENT_STOP);
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
client->state = DHCP_STATE_STOPPED;
return 0;

View file

@ -316,10 +316,14 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
else {
char *string;
if (memchr(option, 0, len))
/*
* One trailing NUL byte is OK, we don't mind. See:
* https://github.com/systemd/systemd/issues/1337
*/
if (memchr(option, 0, len - 1))
return -EINVAL;
string = strndup((const char *)option, len);
string = strndup((const char *) option, len);
if (!string)
return -ENOMEM;

View file

@ -129,6 +129,8 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
assert_return(client, -EINVAL);
assert_return(interface_index >= -1, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
client->index = interface_index;
return 0;
@ -144,6 +146,8 @@ int sd_dhcp6_client_set_mac(
assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
assert_return(arp_type > 0, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
if (arp_type == ARPHRD_ETHER)
assert_return(addr_len == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
@ -177,6 +181,8 @@ int sd_dhcp6_client_set_duid(
assert_return(duid, -EINVAL);
assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
switch (type) {
case DHCP6_DUID_LLT:
if (duid_len <= sizeof(client->duid.llt))
@ -209,6 +215,8 @@ int sd_dhcp6_client_set_duid(
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) {
assert_return(client, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
client->information_request = enabled;
return 0;
@ -270,13 +278,18 @@ static void client_notify(sd_dhcp6_client *client, int event) {
client->cb(client, event, client->userdata);
}
static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
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);
if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
client->lease = sd_dhcp6_lease_unref(client->lease);
}
client_set_lease(client, NULL);
client->receive_message =
sd_event_source_unref(client->receive_message);
@ -468,7 +481,7 @@ static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
state = client->state;
client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
/* RFC 3315, section 18.1.4., says that "...the client may choose to
use a Solicit message to locate a new DHCP server..." */
@ -558,7 +571,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
if (max_retransmit_count &&
client->retransmit_count >= max_retransmit_count) {
client_stop(client, DHCP6_EVENT_RETRANS_MAX);
client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
return 0;
}
@ -830,12 +843,7 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
return 0;
}
if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
client->lease = sd_dhcp6_lease_unref(client->lease);
}
client->lease = lease;
client_set_lease(client, lease);
lease = NULL;
return DHCP6_STATE_BOUND;
@ -864,11 +872,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
if (r < 0 || pref_advertise > pref_lease) {
if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
sd_dhcp6_lease_unref(client->lease);
}
client->lease = lease;
client_set_lease(client, lease);
lease = NULL;
r = 0;
}
@ -937,7 +941,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
if (r < 0)
return 0;
client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
client_start(client, DHCP6_STATE_STOPPED);
@ -969,7 +973,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
return 0;
}
client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
}
break;
@ -1120,7 +1124,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
client_stop(client, DHCP6_EVENT_STOP);
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
return 0;
}
@ -1133,6 +1137,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
return -EALREADY;
r = client_reset(client);
if (r < 0)
return r;
@ -1240,7 +1247,6 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
client_reset(client);
sd_dhcp6_client_detach_event(client);
sd_dhcp6_lease_unref(client->lease);
free(client->req_opts);
free(client);

View file

@ -0,0 +1,531 @@
/***
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
Copyright (C) 2015 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-util.h"
#include "in-addr-util.h"
#include "list.h"
#include "refcnt.h"
#include "random-util.h"
#include "siphash24.h"
#include "util.h"
#include "arp-util.h"
#include "sd-ipv4acd.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__)
typedef enum IPv4ACDState {
IPV4ACD_STATE_INIT,
IPV4ACD_STATE_WAITING_PROBE,
IPV4ACD_STATE_PROBING,
IPV4ACD_STATE_WAITING_ANNOUNCE,
IPV4ACD_STATE_ANNOUNCING,
IPV4ACD_STATE_RUNNING,
_IPV4ACD_STATE_MAX,
_IPV4ACD_STATE_INVALID = -1
} IPv4ACDState;
struct sd_ipv4acd {
RefCount n_ref;
IPv4ACDState state;
int index;
int fd;
int iteration;
int conflict;
sd_event_source *receive_message;
sd_event_source *timer;
usec_t defend_window;
be32_t address;
/* External */
struct ether_addr mac_addr;
sd_event *event;
int event_priority;
sd_ipv4acd_cb_t cb;
void* userdata;
};
sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
if (ll)
assert_se(REFCNT_INC(ll->n_ref) >= 2);
return ll;
}
sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
if (!ll || REFCNT_DEC(ll->n_ref) > 0)
return NULL;
ll->receive_message = sd_event_source_unref(ll->receive_message);
ll->fd = safe_close(ll->fd);
ll->timer = sd_event_source_unref(ll->timer);
sd_ipv4acd_detach_event(ll);
free(ll);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref);
#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp)
int sd_ipv4acd_new(sd_ipv4acd **ret) {
_cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL;
assert_return(ret, -EINVAL);
ll = new0(sd_ipv4acd, 1);
if (!ll)
return -ENOMEM;
ll->n_ref = REFCNT_INIT;
ll->state = IPV4ACD_STATE_INIT;
ll->index = -1;
ll->fd = -1;
*ret = ll;
ll = NULL;
return 0;
}
static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
assert(ll);
assert(st < _IPV4ACD_STATE_MAX);
if (st == ll->state && !reset_counter)
ll->iteration++;
else {
ll->state = st;
ll->iteration = 0;
}
}
static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
assert(ll);
if (ll->cb)
ll->cb(ll, event, ll->userdata);
}
static void ipv4acd_stop(sd_ipv4acd *ll) {
assert(ll);
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);
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) {
_cleanup_event_source_unref_ sd_event_source *timer = NULL;
usec_t next_timeout;
usec_t time_now;
int r;
assert(sec >= 0);
assert(random_sec >= 0);
assert(ll);
next_timeout = sec * USEC_PER_SEC;
if (random_sec)
next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
assert_se(sd_event_now(ll->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);
if (r < 0)
return r;
r = sd_event_source_set_priority(timer, ll->event_priority);
if (r < 0)
return r;
r = sd_event_source_set_description(timer, "ipv4acd-timer");
if (r < 0)
return r;
ll->timer = sd_event_source_unref(ll->timer);
ll->timer = timer;
timer = NULL;
return 0;
}
static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
assert(ll);
assert(arp);
/* see the BPF */
if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
return true;
/* the TPA matched instead of the SPA, this is not a conflict */
return false;
}
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
sd_ipv4acd *ll = userdata;
int r = 0;
assert(ll);
switch (ll->state) {
case IPV4ACD_STATE_INIT:
ipv4acd_set_state(ll, 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 (r < 0)
goto out;
ll->conflict = 0;
} else {
r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
if (r < 0)
goto out;
}
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);
if (r < 0) {
log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
goto out;
} else {
_cleanup_free_ char *address = NULL;
union in_addr_union addr = { .in.s_addr = ll->address };
r = in_addr_to_string(AF_INET, &addr, &address);
if (r >= 0)
log_ipv4acd_debug(ll, "Probing %s", address);
}
if (ll->iteration < PROBE_NUM - 2) {
ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
if (r < 0)
goto out;
} else {
ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
if (r < 0)
goto out;
}
break;
case IPV4ACD_STATE_ANNOUNCING:
if (ll->iteration >= ANNOUNCE_NUM - 1) {
ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
break;
}
case IPV4ACD_STATE_WAITING_ANNOUNCE:
/* Send announcement packet */
r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
if (r < 0) {
log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
goto out;
} else
log_ipv4acd_debug(ll, "ANNOUNCE");
ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
if (r < 0)
goto out;
if (ll->iteration == 0) {
ll->conflict = 0;
ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
}
break;
default:
assert_not_reached("Invalid state.");
}
out:
if (r < 0)
sd_ipv4acd_stop(ll);
return 1;
}
static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
_cleanup_free_ char *address = NULL;
union in_addr_union addr = { .in.s_addr = ll->address };
int r;
assert(ll);
ll->conflict++;
r = in_addr_to_string(AF_INET, &addr, &address);
if (r >= 0)
log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
ipv4acd_stop(ll);
ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
}
static int ipv4acd_on_packet(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
sd_ipv4acd *ll = userdata;
struct ether_arp packet;
int r;
assert(ll);
assert(fd >= 0);
r = read(fd, &packet, sizeof(struct ether_arp));
if (r < (int) sizeof(struct ether_arp))
goto out;
switch (ll->state) {
case IPV4ACD_STATE_ANNOUNCING:
case IPV4ACD_STATE_RUNNING:
if (ipv4acd_arp_conflict(ll, &packet)) {
usec_t ts;
assert_se(sd_event_now(ll->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 (r < 0) {
log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
goto out;
} else
log_ipv4acd_debug(ll, "DEFEND");
} else
ipv4acd_on_conflict(ll);
}
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);
break;
default:
assert_not_reached("Invalid state.");
}
out:
if (r < 0)
sd_ipv4acd_stop(ll);
return 1;
}
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);
ll->index = interface_index;
return 0;
}
int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
assert_return(ll, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
memcpy(&ll->mac_addr, addr, ETH_ALEN);
return 0;
}
int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
assert_return(ll, -EINVAL);
ll->event = sd_event_unref(ll->event);
return 0;
}
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
int r;
assert_return(ll, -EINVAL);
assert_return(!ll->event, -EBUSY);
if (event)
ll->event = sd_event_ref(event);
else {
r = sd_event_default(&ll->event);
if (r < 0)
return r;
}
ll->event_priority = priority;
return 0;
}
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
assert_return(ll, -EINVAL);
ll->cb = cb;
ll->userdata = userdata;
return 0;
}
int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
assert_return(ll, -EINVAL);
assert_return(address, -EINVAL);
assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
ll->address = address->s_addr;
return 0;
}
bool sd_ipv4acd_is_running(sd_ipv4acd *ll) {
assert_return(ll, false);
return ll->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 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);
ll->defend_window = 0;
r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->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;
}
return 0;
}

View file

@ -2,6 +2,7 @@
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
Copyright (C) 2015 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@ -25,429 +26,153 @@
#include <stdio.h>
#include <arpa/inet.h>
#include "util.h"
#include "siphash24.h"
#include "event-util.h"
#include "list.h"
#include "random-util.h"
#include "refcnt.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "util.h"
#include "ipv4ll-internal.h"
#include "sd-ipv4acd.h"
#include "sd-ipv4ll.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 IPV4LL_NETWORK 0xA9FE0000L
#define IPV4LL_NETMASK 0xFFFF0000L
typedef enum IPv4LLTrigger{
IPV4LL_TRIGGER_NULL,
IPV4LL_TRIGGER_PACKET,
IPV4LL_TRIGGER_TIMEOUT,
_IPV4LL_TRIGGER_MAX,
_IPV4LL_TRIGGER_INVALID = -1
} IPv4LLTrigger;
typedef enum IPv4LLState {
IPV4LL_STATE_INIT,
IPV4LL_STATE_WAITING_PROBE,
IPV4LL_STATE_PROBING,
IPV4LL_STATE_WAITING_ANNOUNCE,
IPV4LL_STATE_ANNOUNCING,
IPV4LL_STATE_RUNNING,
IPV4LL_STATE_STOPPED,
_IPV4LL_STATE_MAX,
_IPV4LL_STATE_INVALID = -1
} IPv4LLState;
#define IPV4LL_DONT_DESTROY(ll) \
_cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
struct sd_ipv4ll {
unsigned n_ref;
IPv4LLState state;
int index;
int fd;
union sockaddr_union link;
int iteration;
int conflict;
sd_event_source *receive_message;
sd_event_source *timer;
usec_t next_wakeup;
usec_t defend_window;
int next_wakeup_valid;
be32_t address;
sd_ipv4acd *acd;
be32_t address; /* the address pushed to ACD */
struct random_data *random_data;
char *random_data_state;
/* External */
be32_t claimed_address;
struct ether_addr mac_addr;
sd_event *event;
int event_priority;
sd_ipv4ll_cb_t cb;
void* userdata;
};
static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data);
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
if (!ll)
return NULL;
static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) {
assert(ll);
assert(st < _IPV4LL_STATE_MAX);
if (st == ll->state && !reset_counter) {
ll->iteration++;
} else {
ll->state = st;
ll->iteration = 0;
}
}
static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
assert(ll);
if (ll->cb) {
ll = sd_ipv4ll_ref(ll);
ll->cb(ll, event, ll->userdata);
ll = sd_ipv4ll_unref(ll);
}
assert(ll->n_ref >= 1);
ll->n_ref++;
return ll;
}
static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) {
assert(ll);
sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
if (!ll)
return NULL;
ll->receive_message = sd_event_source_unref(ll->receive_message);
ll->fd = safe_close(ll->fd);
assert(ll->n_ref >= 1);
ll->n_ref--;
ll->timer = sd_event_source_unref(ll->timer);
if (ll->n_ref > 0)
return NULL;
log_ipv4ll(ll, "STOPPED");
sd_ipv4acd_unref(ll->acd);
ll = ipv4ll_client_notify(ll, event);
free(ll->random_data);
free(ll->random_data_state);
free(ll);
if (ll) {
ll->claimed_address = 0;
ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
}
return ll;
return NULL;
}
static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
be32_t addr;
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
int sd_ipv4ll_new(sd_ipv4ll **ret) {
_cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
int r;
int32_t random;
assert(ll);
assert(address);
assert(ll->random_data);
assert_return(ret, -EINVAL);
do {
r = random_r(ll->random_data, &random);
if (r < 0)
return r;
addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
} while (addr == ll->address ||
(ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
(ntohl(addr) & 0x0000FF00) == 0x0000 ||
(ntohl(addr) & 0x0000FF00) == 0xFF00);
ll = new0(sd_ipv4ll, 1);
if (!ll)
return -ENOMEM;
*address = addr;
return 0;
}
static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
assert(ll);
ll->next_wakeup_valid = 0;
ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL);
return 0;
}
static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
usec_t next_timeout = 0;
usec_t time_now = 0;
assert(sec >= 0);
assert(random_sec >= 0);
assert(ll);
next_timeout = sec * USEC_PER_SEC;
if (random_sec)
next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
ll->next_wakeup = time_now + next_timeout;
ll->next_wakeup_valid = 1;
}
static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
assert(ll);
assert(arp);
if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 &&
memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0)
return true;
return false;
}
static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
assert(ll);
assert(arp);
if (ipv4ll_arp_conflict(ll, arp))
return true;
if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 &&
memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN))
return true;
return false;
}
static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) {
struct ether_arp out_packet;
int out_packet_ready = 0;
int r = 0;
assert(ll);
assert(trigger < _IPV4LL_TRIGGER_MAX);
if (ll->state == IPV4LL_STATE_INIT) {
log_ipv4ll(ll, "PROBE");
ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
} else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
(ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) {
/* Send a probe */
arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
out_packet_ready = 1;
ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0);
ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
} else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) {
/* Send the last probe */
arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
out_packet_ready = 1;
ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1);
ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
} else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
(ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) {
/* Send announcement packet */
arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
out_packet_ready = 1;
ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0);
ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
if (ll->iteration == 0) {
log_ipv4ll(ll, "ANNOUNCE");
ll->claimed_address = ll->address;
ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
if (!ll || ll->state == IPV4LL_STATE_STOPPED)
goto out;
ll->conflict = 0;
}
} else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT &&
ll->iteration >= ANNOUNCE_NUM-1)) {
ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0);
ll->next_wakeup_valid = 0;
} else if (trigger == IPV4LL_TRIGGER_PACKET) {
int conflicted = 0;
usec_t time_now;
struct ether_arp* in_packet = (struct ether_arp*)trigger_data;
assert(in_packet);
if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) {
if (ipv4ll_arp_conflict(ll, in_packet)) {
r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
goto out;
/* Defend address */
if (time_now > ll->defend_window) {
ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC;
arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
out_packet_ready = 1;
} else
conflicted = 1;
}
} else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE,
IPV4LL_STATE_PROBING,
IPV4LL_STATE_WAITING_ANNOUNCE)) {
conflicted = ipv4ll_arp_probe_conflict(ll, in_packet);
}
if (conflicted) {
log_ipv4ll(ll, "CONFLICT");
ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
if (!ll || ll->state == IPV4LL_STATE_STOPPED)
goto out;
ll->claimed_address = 0;
/* Pick a new address */
r = ipv4ll_pick_address(ll, &ll->address);
if (r < 0)
goto out;
ll->conflict++;
ll->defend_window = 0;
ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
if (ll->conflict >= MAX_CONFLICTS) {
log_ipv4ll(ll, "MAX_CONFLICTS");
ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
} else
ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
}
}
if (out_packet_ready) {
r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet);
if (r < 0) {
log_ipv4ll(ll, "failed to send arp packet out");
goto out;
}
}
if (ll->next_wakeup_valid) {
ll->timer = sd_event_source_unref(ll->timer);
r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(),
ll->next_wakeup, 0, ipv4ll_timer, ll);
if (r < 0)
goto out;
r = sd_event_source_set_priority(ll->timer, ll->event_priority);
if (r < 0)
goto out;
r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
if (r < 0)
goto out;
}
out:
if (r < 0 && ll)
ipv4ll_stop(ll, r);
}
static int ipv4ll_receive_message(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
int r;
struct ether_arp arp;
sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
assert(ll);
r = read(fd, &arp, sizeof(struct ether_arp));
if (r < (int) sizeof(struct ether_arp))
return 0;
r = arp_packet_verify_headers(&arp);
r = sd_ipv4acd_new(&ll->acd);
if (r < 0)
return 0;
return r;
ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp);
r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
if (r < 0)
return r;
ll->n_ref = 1;
*ret = ll;
ll = NULL;
return 0;
}
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;
}
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
assert_return(ll, -EINVAL);
assert_return(interface_index > 0, -EINVAL);
assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
IPV4LL_STATE_STOPPED), -EBUSY);
ll->index = interface_index;
return 0;
return sd_ipv4acd_set_index(ll->acd, interface_index);
}
#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) {
bool need_restart = false;
int r;
assert_return(ll, -EINVAL);
assert_return(addr, -EINVAL);
if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
return 0;
if (!ll->random_data) {
uint8_t seed[8];
if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) {
log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
"client, restarting");
ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
need_restart = true;
/* If no random data is set, generate some from the MAC */
siphash24(seed, &addr->ether_addr_octet,
ETH_ALEN, HASH_KEY.bytes);
assert_cc(sizeof(unsigned) <= 8);
r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
if (r < 0)
return r;
}
if (!ll)
return 0;
memcpy(&ll->mac_addr, addr, ETH_ALEN);
if (need_restart)
sd_ipv4ll_start(ll);
return 0;
return sd_ipv4acd_set_mac(ll->acd, addr);
}
int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
assert_return(ll, -EINVAL);
ll->event = sd_event_unref(ll->event);
return 0;
return sd_ipv4acd_detach_event(ll->acd);
}
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
int r;
assert_return(ll, -EINVAL);
assert_return(!ll->event, -EBUSY);
if (event)
ll->event = sd_event_ref(event);
else {
r = sd_event_default(&ll->event);
if (r < 0) {
ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
return r;
}
}
ll->event_priority = priority;
r = sd_ipv4acd_attach_event(ll->acd, event, priority);
if (r < 0)
return r;
return 0;
}
@ -469,189 +194,146 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
return -ENOENT;
address->s_addr = ll->claimed_address;
return 0;
}
int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) {
unsigned int entropy;
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;
assert_return(ll, -EINVAL);
assert_return(seed, -EINVAL);
entropy = *seed;
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->random_data = new0(struct random_data, 1);
ll->random_data_state = new0(char, 128);
if (!ll->random_data || !ll->random_data_state) {
r = -ENOMEM;
goto error;
}
r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
if (r < 0)
goto error;
error:
if (r < 0){
free(ll->random_data);
free(ll->random_data_state);
ll->random_data = NULL;
ll->random_data_state = NULL;
}
return r;
return 0;
}
bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
assert_return(ll, false);
return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
return sd_ipv4acd_is_running(ll->acd);
}
#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
static int ipv4ll_pick_address(sd_ipv4ll *ll) {
struct in_addr in_addr;
be32_t addr;
int r;
int32_t random;
int sd_ipv4ll_start (sd_ipv4ll *ll) {
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);
} while (addr == ll->address ||
(ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
(ntohl(addr) & 0x0000FF00) == 0x0000 ||
(ntohl(addr) & 0x0000FF00) == 0xFF00);
in_addr.s_addr = addr;
r = sd_ipv4acd_set_address(ll->acd, &in_addr);
if (r < 0)
return r;
ll->address = addr;
return 0;
}
int sd_ipv4ll_start(sd_ipv4ll *ll) {
int r;
assert_return(ll, -EINVAL);
assert_return(ll->event, -EINVAL);
assert_return(ll->index > 0, -EINVAL);
assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
IPV4LL_STATE_STOPPED), -EBUSY);
ll->state = IPV4LL_STATE_INIT;
r = arp_network_bind_raw_socket(ll->index, &ll->link);
if (r < 0)
goto out;
ll->fd = r;
ll->conflict = 0;
ll->defend_window = 0;
ll->claimed_address = 0;
if (!ll->random_data) {
uint8_t seed[8];
/* Fallback to mac */
siphash24(seed, &ll->mac_addr.ether_addr_octet,
ETH_ALEN, HASH_KEY.bytes);
r = sd_ipv4ll_set_address_seed(ll, seed);
if (r < 0)
goto out;
}
assert_return(ll->random_data, -EINVAL);
if (ll->address == 0) {
r = ipv4ll_pick_address(ll, &ll->address);
r = ipv4ll_pick_address(ll);
if (r < 0)
goto out;
return r;
}
ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
EPOLLIN, ipv4ll_receive_message, ll);
r = sd_ipv4acd_start(ll->acd);
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, "ipv4ll-receive-message");
if (r < 0)
goto out;
r = sd_event_add_time(ll->event,
&ll->timer,
clock_boottime_or_monotonic(),
now(clock_boottime_or_monotonic()), 0,
ipv4ll_timer, ll);
if (r < 0)
goto out;
r = sd_event_source_set_priority(ll->timer, ll->event_priority);
if (r < 0)
goto out;
r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
out:
if (r < 0)
ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
return r;
return 0;
}
int sd_ipv4ll_stop(sd_ipv4ll *ll) {
ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
if (ll)
ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1);
static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
assert(ll);
return 0;
if (ll->cb)
ll->cb(ll, event, ll->userdata);
}
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
sd_ipv4ll *ll = userdata;
IPV4LL_DONT_DESTROY(ll);
int r;
if (!ll)
return NULL;
assert(acd);
assert(ll);
assert(ll->n_ref >= 1);
ll->n_ref++;
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
return ll;
}
sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
if (!ll)
return NULL;
assert(ll->n_ref >= 1);
ll->n_ref--;
if (ll->n_ref > 0)
return ll;
ll->receive_message = sd_event_source_unref(ll->receive_message);
ll->fd = safe_close(ll->fd);
ll->timer = sd_event_source_unref(ll->timer);
sd_ipv4ll_detach_event(ll);
free(ll->random_data);
free(ll->random_data_state);
free(ll);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
int sd_ipv4ll_new(sd_ipv4ll **ret) {
_cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
assert_return(ret, -EINVAL);
ll = new0(sd_ipv4ll, 1);
if (!ll)
return -ENOMEM;
ll->n_ref = 1;
ll->state = IPV4LL_STATE_INIT;
ll->index = -1;
ll->fd = -1;
*ret = ll;
ll = NULL;
return 0;
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 */
if (ll->claimed_address != 0) {
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
ll->claimed_address = 0;
} else {
r = ipv4ll_pick_address(ll);
if (r < 0)
goto error;
r = sd_ipv4acd_start(ll->acd);
if (r < 0)
goto error;
}
break;
default:
assert_not_reached("Invalid IPv4ACD event.");
}
return;
error:
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
}

View file

@ -0,0 +1,736 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <arpa/inet.h>
#include "siphash24.h"
#include "hashmap.h"
#include "lldp-tlv.h"
#include "lldp-port.h"
#include "sd-lldp.h"
#include "prioq.h"
#include "lldp-internal.h"
#include "lldp-util.h"
typedef enum LLDPAgentRXState {
LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
LLDP_AGENT_RX_DELETE_AGED_INFO,
LLDP_AGENT_RX_LLDP_INITIALIZE,
LLDP_AGENT_RX_WAIT_FOR_FRAME,
LLDP_AGENT_RX_RX_FRAME,
LLDP_AGENT_RX_DELETE_INFO,
LLDP_AGENT_RX_UPDATE_INFO,
_LLDP_AGENT_RX_STATE_MAX,
_LLDP_AGENT_RX_INVALID = -1,
} LLDPAgentRXState;
/* Section 10.5.2.2 Reception counters */
struct lldp_agent_statistics {
uint64_t stats_ageouts_total;
uint64_t stats_frames_discarded_total;
uint64_t stats_frames_in_errors_total;
uint64_t stats_frames_in_total;
uint64_t stats_tlvs_discarded_total;
uint64_t stats_tlvs_unrecognized_total;
};
struct sd_lldp {
lldp_port *port;
Prioq *by_expiry;
Hashmap *neighbour_mib;
sd_lldp_cb_t cb;
void *userdata;
LLDPAgentRXState rx_state;
lldp_agent_statistics statistics;
};
static void chassis_id_hash_func(const void *p, struct siphash *state) {
const lldp_chassis_id *id = p;
assert(id);
assert(id->data);
siphash24_compress(&id->length, sizeof(id->length), state);
siphash24_compress(id->data, id->length, state);
}
static int chassis_id_compare_func(const void *_a, const void *_b) {
const lldp_chassis_id *a, *b;
a = _a;
b = _b;
assert(!a->length || a->data);
assert(!b->length || b->data);
if (a->type != b->type)
return -1;
if (a->length != b->length)
return a->length < b->length ? -1 : 1;
return memcmp(a->data, b->data, a->length);
}
static const struct hash_ops chassis_id_hash_ops = {
.hash = chassis_id_hash_func,
.compare = chassis_id_compare_func
};
static void lldp_mib_delete_objects(sd_lldp *lldp);
static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state);
static void lldp_run_state_machine(sd_lldp *ll);
static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
int r;
assert(lldp);
assert(tlv);
/* Remove expired packets */
if (prioq_size(lldp->by_expiry) > 0) {
lldp_set_state(lldp, LLDP_AGENT_RX_DELETE_INFO);
lldp_mib_delete_objects(lldp);
}
r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv);
if (r < 0)
goto out;
lldp_set_state(lldp, LLDP_AGENT_RX_UPDATE_INFO);
log_lldp("Packet added. MIB size: %d , PQ size: %d",
hashmap_size(lldp->neighbour_mib),
prioq_size(lldp->by_expiry));
lldp->statistics.stats_frames_in_total ++;
out:
if (r < 0)
log_lldp("Receive frame failed: %s", strerror(-r));
lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
return 0;
}
/* 10.3.2 LLDPDU validation: rxProcessFrame() */
int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
uint16_t type, len, i, l, t;
bool chassis_id = false;
bool malformed = false;
bool port_id = false;
bool ttl = false;
bool end = false;
lldp_port *port;
uint8_t *p, *q;
sd_lldp *lldp;
int r;
assert(tlv);
assert(length > 0);
port = (lldp_port *) tlv->userdata;
lldp = (sd_lldp *) port->userdata;
if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
log_lldp("Port is disabled : %s . Dropping ...",
lldp->port->ifname);
goto out;
}
lldp_set_state(lldp, LLDP_AGENT_RX_RX_FRAME);
p = tlv->pdu;
p += sizeof(struct ether_header);
for (i = 1, l = 0; l <= length; i++) {
memcpy(&t, p, sizeof(uint16_t));
type = ntohs(t) >> 9;
len = ntohs(t) & 0x01ff;
if (type == LLDP_TYPE_END) {
if (len != 0) {
log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
len);
malformed = true;
goto out;
}
end = true;
break;
} else if (type >=_LLDP_TYPE_MAX) {
log_lldp("TLV type not recognized %d . Dropping ...",
type);
malformed = true;
goto out;
}
/* skip type and length encoding */
p += 2;
q = p;
p += len;
l += (len + 2);
if (i <= 3) {
if (i != type) {
log_lldp("TLV missing or out of order. Dropping ...");
malformed = true;
goto out;
}
}
switch(type) {
case LLDP_TYPE_CHASSIS_ID:
if (len < 2) {
log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
len);
malformed = true;
goto out;
}
if (chassis_id) {
log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
malformed = true;
goto out;
}
/* Look what subtype it has */
if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED ||
*q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
*q);
malformed = true;
goto out;
}
chassis_id = true;
break;
case LLDP_TYPE_PORT_ID:
if (len < 2) {
log_lldp("Received malformed Port ID TLV len = %d. Dropping",
len);
malformed = true;
goto out;
}
if (port_id) {
log_lldp("Duplicate Port ID TLV found. Dropping ...");
malformed = true;
goto out;
}
/* Look what subtype it has */
if (*q == LLDP_PORT_SUBTYPE_RESERVED ||
*q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
*q);
malformed = true;
goto out;
}
port_id = true;
break;
case LLDP_TYPE_TTL:
if(len != 2) {
log_lldp(
"Received invalid lenth: %d TTL TLV. Dropping ...",
len);
malformed = true;
goto out;
}
if (ttl) {
log_lldp("Duplicate TTL TLV found. Dropping ...");
malformed = true;
goto out;
}
ttl = true;
break;
default:
if (len == 0) {
log_lldp("TLV type = %d's, length 0 received . Dropping ...",
type);
malformed = true;
goto out;
}
break;
}
}
if(!chassis_id || !port_id || !ttl || !end) {
log_lldp( "One or more mandotory TLV missing . Dropping ...");
malformed = true;
goto out;
}
r = tlv_packet_parse_pdu(tlv, length);
if (r < 0) {
log_lldp( "Failed to parse the TLV. Dropping ...");
malformed = true;
goto out;
}
return lldp_receive_frame(lldp, tlv);
out:
lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
if (malformed) {
lldp->statistics.stats_frames_discarded_total ++;
lldp->statistics.stats_frames_in_errors_total ++;
}
sd_lldp_packet_unref(tlv);
return 0;
}
static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) {
const lldp_neighbour_port *p = a, *q = b;
if (p->until < q->until)
return -1;
if (p->until > q->until)
return 1;
return 0;
}
static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) {
assert(lldp);
assert(state < _LLDP_AGENT_RX_STATE_MAX);
lldp->rx_state = state;
lldp_run_state_machine(lldp);
}
static void lldp_run_state_machine(sd_lldp *lldp) {
if (!lldp->cb)
return;
switch (lldp->rx_state) {
case LLDP_AGENT_RX_UPDATE_INFO:
lldp->cb(lldp, SD_LLDP_EVENT_UPDATE_INFO, lldp->userdata);
break;
default:
break;
}
}
/* 10.5.5.2.1 mibDeleteObjects ()
* The mibDeleteObjects () procedure deletes all information in the LLDP remote
* systems MIB associated with the MSAP identifier if an LLDPDU is received with
* an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
static void lldp_mib_delete_objects(sd_lldp *lldp) {
lldp_neighbour_port *p;
usec_t t = 0;
/* Remove all entries that are past their TTL */
for (;;) {
if (prioq_size(lldp->by_expiry) <= 0)
break;
p = prioq_peek(lldp->by_expiry);
if (!p)
break;
if (t <= 0)
t = now(clock_boottime_or_monotonic());
if (p->until > t)
break;
lldp_neighbour_port_remove_and_free(p);
lldp->statistics.stats_ageouts_total ++;
}
}
static void lldp_mib_objects_flush(sd_lldp *lldp) {
lldp_neighbour_port *p, *q;
lldp_chassis *c;
assert(lldp);
assert(lldp->neighbour_mib);
assert(lldp->by_expiry);
/* Drop all packets */
while ((c = hashmap_steal_first(lldp->neighbour_mib))) {
LIST_FOREACH_SAFE(port, p, q, c->ports) {
lldp_neighbour_port_remove_and_free(p);
}
}
assert(hashmap_size(lldp->neighbour_mib) == 0);
assert(prioq_size(lldp->by_expiry) == 0);
}
int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
uint8_t *mac, *port_id, type;
lldp_neighbour_port *p;
uint16_t data = 0, length = 0;
char buf[LINE_MAX];
lldp_chassis *c;
usec_t time;
Iterator i;
int r;
assert(lldp);
assert(lldp_file);
r = fopen_temporary(lldp_file, &f, &temp_path);
if (r < 0)
goto fail;
fchmod(fileno(f), 0644);
HASHMAP_FOREACH(c, lldp->neighbour_mib, i) {
LIST_FOREACH(port, p, c->ports) {
_cleanup_free_ char *s = NULL;
char *k, *t;
r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length);
if (r < 0)
continue;
sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
s = strdup(buf);
if (!s) {
r = -ENOMEM;
goto fail;
}
r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length);
if (r < 0)
continue;
if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
k = strndup((char *) port_id, length -1);
if (!k) {
r = -ENOMEM;
goto fail;
}
sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
free(k);
} else {
mac = port_id;
sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
}
k = strappend(s, buf);
if (!k) {
r = -ENOMEM;
goto fail;
}
free(s);
s = k;
time = now(clock_boottime_or_monotonic());
/* Don't write expired packets */
if (time - p->until <= 0)
continue;
sprintf(buf, "'_TTL="USEC_FMT"' ", p->until);
k = strappend(s, buf);
if (!k) {
r = -ENOMEM;
goto fail;
}
free(s);
s = k;
r = sd_lldp_packet_read_system_name(p->packet, &k, &length);
if (r < 0)
k = strappend(s, "'_NAME=N/A' ");
else {
t = strndup(k, length);
if (!t) {
r = -ENOMEM;
goto fail;
}
k = strjoin(s, "'_NAME=", t, "' ", NULL);
free(t);
}
if (!k) {
r = -ENOMEM;
goto fail;
}
free(s);
s = k;
(void) sd_lldp_packet_read_system_capability(p->packet, &data);
sprintf(buf, "'_CAP=%x'", data);
k = strappend(s, buf);
if (!k) {
r = -ENOMEM;
goto fail;
}
free(s);
s = k;
fprintf(f, "%s\n", s);
}
}
r = fflush_and_check(f);
if (r < 0)
goto fail;
if (rename(temp_path, lldp_file) < 0) {
r = -errno;
goto fail;
}
return 0;
fail:
if (temp_path)
(void) unlink(temp_path);
return log_error_errno(r, "Failed to save lldp data %s: %m", lldp_file);
}
int sd_lldp_start(sd_lldp *lldp) {
int r;
assert_return(lldp, -EINVAL);
assert_return(lldp->port, -EINVAL);
lldp->port->status = LLDP_PORT_STATUS_ENABLED;
lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE);
r = lldp_port_start(lldp->port);
if (r < 0) {
log_lldp("Failed to start Port : %s , %s",
lldp->port->ifname,
strerror(-r));
lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL);
return r;
}
lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
return 0;
}
int sd_lldp_stop(sd_lldp *lldp) {
int r;
assert_return(lldp, -EINVAL);
assert_return(lldp->port, -EINVAL);
lldp->port->status = LLDP_PORT_STATUS_DISABLED;
r = lldp_port_stop(lldp->port);
if (r < 0)
return r;
lldp_mib_objects_flush(lldp);
return 0;
}
int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
int r;
assert_return(lldp, -EINVAL);
assert_return(!lldp->port->event, -EBUSY);
if (event)
lldp->port->event = sd_event_ref(event);
else {
r = sd_event_default(&lldp->port->event);
if (r < 0)
return r;
}
lldp->port->event_priority = priority;
return 0;
}
int sd_lldp_detach_event(sd_lldp *lldp) {
assert_return(lldp, -EINVAL);
lldp->port->event = sd_event_unref(lldp->port->event);
return 0;
}
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
assert_return(lldp, -EINVAL);
lldp->cb = cb;
lldp->userdata = userdata;
return 0;
}
void sd_lldp_free(sd_lldp *lldp) {
if (!lldp)
return;
/* Drop all packets */
lldp_mib_objects_flush(lldp);
lldp_port_free(lldp->port);
hashmap_free(lldp->neighbour_mib);
prioq_free(lldp->by_expiry);
free(lldp);
}
int sd_lldp_new(int ifindex,
const char *ifname,
const struct ether_addr *mac,
sd_lldp **ret) {
_cleanup_lldp_free_ sd_lldp *lldp = NULL;
int r;
assert_return(ret, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
assert_return(ifname, -EINVAL);
assert_return(mac, -EINVAL);
lldp = new0(sd_lldp, 1);
if (!lldp)
return -ENOMEM;
r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
if (r < 0)
return r;
lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
if (!lldp->neighbour_mib)
return -ENOMEM;
r = prioq_ensure_allocated(&lldp->by_expiry,
ttl_expiry_item_prioq_compare_func);
if (r < 0)
return r;
lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL;
*ret = lldp;
lldp = NULL;
return 0;
}
int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) {
lldp_neighbour_port *p;
lldp_chassis *c;
Iterator iter;
unsigned count = 0, i;
assert_return(lldp, -EINVAL);
assert_return(tlvs, -EINVAL);
HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
LIST_FOREACH(port, p, c->ports)
count++;
}
if (!count) {
*tlvs = NULL;
return 0;
}
*tlvs = new(sd_lldp_packet *, count);
if (!*tlvs)
return -ENOMEM;
i = 0;
HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
LIST_FOREACH(port, p, c->ports)
(*tlvs)[i++] = sd_lldp_packet_ref(p->packet);
}
return count;
}

View file

@ -0,0 +1,33 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "util.h"
#include "sd-event.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event*, sd_event_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, sd_event_source_unref);
#define _cleanup_event_unref_ _cleanup_(sd_event_unrefp)
#define _cleanup_event_source_unref_ _cleanup_(sd_event_source_unrefp)

View file

@ -31,7 +31,7 @@
#include "random-util.h"
#if 0 /* NM_IGNORED */
_public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) {
_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
unsigned n;
assert_return(s, NULL);

View file

@ -382,9 +382,8 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
}
#if 0 /* NM_IGNORED */
unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
void dns_name_hash_func(const void *s, struct siphash *state) {
const char *p = s;
unsigned long ul = hash_key[0];
int r;
assert(p);
@ -403,13 +402,17 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_
if (k > 0)
r = k;
if (r == 0)
break;
label[r] = 0;
ascii_strlower(label);
ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
string_hash_func(label, state);
}
return ul;
/* enforce that all names are terminated by the empty label */
string_hash_func("", state);
}
int dns_name_compare_func(const void *a, const void *b) {

View file

@ -58,7 +58,7 @@ static inline int dns_name_is_valid(const char *s) {
}
#if 0 /* NM_IGNORED */
unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]);
void dns_name_hash_func(const void *s, struct siphash *state);
int dns_name_compare_func(const void *a, const void *b);
extern const struct hash_ops dns_name_hash_ops;

View file

@ -31,11 +31,11 @@
#include "sd-dhcp-lease.h"
enum {
DHCP_EVENT_STOP = 0,
DHCP_EVENT_IP_ACQUIRE = 1,
DHCP_EVENT_IP_CHANGE = 2,
DHCP_EVENT_EXPIRED = 3,
DHCP_EVENT_RENEW = 4,
SD_DHCP_CLIENT_EVENT_STOP = 0,
SD_DHCP_CLIENT_EVENT_IP_ACQUIRE = 1,
SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2,
SD_DHCP_CLIENT_EVENT_EXPIRED = 3,
SD_DHCP_CLIENT_EVENT_RENEW = 4,
};
typedef struct sd_dhcp_client sd_dhcp_client;

View file

@ -31,11 +31,11 @@
#include "sd-dhcp6-lease.h"
enum {
DHCP6_EVENT_STOP = 0,
DHCP6_EVENT_RESEND_EXPIRE = 10,
DHCP6_EVENT_RETRANS_MAX = 11,
DHCP6_EVENT_IP_ACQUIRE = 12,
DHCP6_EVENT_INFORMATION_REQUEST = 13,
SD_DHCP6_CLIENT_EVENT_STOP = 0,
SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE = 10,
SD_DHCP6_CLIENT_EVENT_RETRANS_MAX = 11,
SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE = 12,
SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13,
};
typedef struct sd_dhcp6_client sd_dhcp6_client;

View file

@ -29,11 +29,11 @@
#include "sd-event.h"
enum {
ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE = 0,
ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1,
ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER = 2,
ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3,
ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4,
SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE = 0,
SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1,
SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER = 2,
SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3,
SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4,
};
typedef struct sd_icmp6_nd sd_icmp6_nd;
@ -66,9 +66,9 @@ int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr,
int sd_icmp6_nd_stop(sd_icmp6_nd *nd);
int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd);
#define SD_ICMP6_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
#define SD_ICMP6_ND_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
#define SD_ICMP6_ADDRESS_FORMAT_VAL(address) \
#define SD_ICMP6_ND_ADDRESS_FORMAT_VAL(address) \
be16toh((address).s6_addr16[0]), \
be16toh((address).s6_addr16[1]), \
be16toh((address).s6_addr16[2]), \

View file

@ -0,0 +1,57 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foosdipv4acdfoo
#define foosdipv4acdfoo
/***
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
Copyright (C) 2015 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <stdbool.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include "sd-event.h"
enum {
SD_IPV4ACD_EVENT_STOP = 0,
SD_IPV4ACD_EVENT_BIND = 1,
SD_IPV4ACD_EVENT_CONFLICT = 2,
};
typedef struct sd_ipv4acd sd_ipv4acd;
typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_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);
bool 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_new (sd_ipv4acd **ret);
#endif

View file

@ -31,9 +31,9 @@
#include "sd-event.h"
enum {
IPV4LL_EVENT_STOP = 0,
IPV4LL_EVENT_BIND = 1,
IPV4LL_EVENT_CONFLICT = 2,
SD_IPV4LL_EVENT_STOP = 0,
SD_IPV4LL_EVENT_BIND = 1,
SD_IPV4LL_EVENT_CONFLICT = 2,
};
typedef struct sd_ipv4ll sd_ipv4ll;
@ -45,7 +45,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_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_address_seed(sd_ipv4ll *ll, uint8_t seed[8]);
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
int sd_ipv4ll_start(sd_ipv4ll *ll);
int sd_ipv4ll_stop(sd_ipv4ll *ll);

View file

@ -0,0 +1,76 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "nm-sd-adapt.h"
#include "sd-event.h"
enum {
SD_LLDP_EVENT_UPDATE_INFO = 0,
};
enum {
SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE,
SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE,
SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE,
};
typedef struct sd_lldp sd_lldp;
typedef struct tlv_packet sd_lldp_packet;
typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata);
int sd_lldp_new(int ifindex, const char *ifname, const struct ether_addr *mac, sd_lldp **ret);
void sd_lldp_free(sd_lldp *lldp);
int sd_lldp_start(sd_lldp *lldp);
int sd_lldp_stop(sd_lldp *lldp);
int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority);
int sd_lldp_detach_event(sd_lldp *lldp);
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata);
int sd_lldp_save(sd_lldp *lldp, const char *file);
int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl);
int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length);
int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data);
int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
/* IEEE 802.1 organizationally specific TLVs */
int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id);
int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id);
int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length);
int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id);
int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id);
sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv);
sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv);
int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest);
int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs);