sd-ndisc.c revision 0d43d2fcb7ac5264c739dc2f67f93ed0985a418a
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan/***
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan This file is part of systemd.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan Copyright (C) 2014 Intel Corporation. All rights reserved.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan systemd is free software; you can redistribute it and/or modify it
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan under the terms of the GNU Lesser General Public License as published by
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan the Free Software Foundation; either version 2.1 of the License, or
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan (at your option) any later version.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan systemd is distributed in the hope that it will be useful, but
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan WITHOUT ANY WARRANTY; without even the implied warranty of
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan Lesser General Public License for more details.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan You should have received a copy of the GNU Lesser General Public License
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan along with systemd; If not, see <http://www.gnu.org/licenses/>.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan***/
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include <netinet/icmp6.h>
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include <netinet/in.h>
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include <netinet/ip6.h>
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include <stdbool.h>
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include <string.h>
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include <sys/ioctl.h>
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan#include "sd-ndisc.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include "alloc-util.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include "async.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include "icmp6-util.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include "in-addr-util.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include "list.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include "socket-util.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#include "string-util.h"
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#define NDISC_MAX_ROUTER_SOLICITATIONS 3
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganenum NDiscState {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan NDISC_STATE_IDLE,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan NDISC_STATE_SOLICITATION_SENT,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan NDISC_STATE_ADVERTISMENT_LISTEN,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan _NDISC_STATE_MAX,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan _NDISC_STATE_INVALID = -1,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan};
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan#define IP6_MIN_MTU (unsigned)1280
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan#define NDISC_OPT_LEN_UNITS 8
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan#define ND_RA_FLAG_PREF 0x18
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan#define ND_RA_FLAG_PREF_LOW 0x03
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan#define ND_RA_FLAG_PREF_MEDIUM 0x0
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#define ND_RA_FLAG_PREF_HIGH 0x1
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#define ND_RA_FLAG_PREF_INVALID 0x2
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogantypedef struct NDiscPrefix NDiscPrefix;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hoganstruct NDiscPrefix {
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan unsigned n_ref;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan sd_ndisc *nd;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan LIST_FIELDS(NDiscPrefix, prefixes);
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan uint8_t len;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan usec_t valid_until;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan struct in6_addr addr;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan};
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganstruct sd_ndisc {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan unsigned n_ref;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan enum NDiscState state;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_event *event;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan int event_priority;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan int index;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan struct ether_addr mac_addr;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan uint32_t mtu;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan LIST_HEAD(NDiscPrefix, prefixes);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan int fd;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_event_source *recv;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_event_source *timeout;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan int nd_sent;
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan sd_ndisc_router_callback_t router_callback;
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback;
55e32e1d339c1e3417aa96111d48d51eb29be585Dirk Hogan sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc_callback_t callback;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan void *userdata;
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan};
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan#define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hoganstatic NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (!prefix)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return NULL;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan assert(prefix->n_ref > 0);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan prefix->n_ref--;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (prefix->n_ref > 0)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return NULL;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (prefix->nd)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan free(prefix);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return NULL;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan}
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hoganstatic int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan _cleanup_free_ NDiscPrefix *prefix = NULL;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
1f08e1627971a4cb3a0d497771fb6b7d2bcb9722Dirk Hogan assert(ret);
1f08e1627971a4cb3a0d497771fb6b7d2bcb9722Dirk Hogan
1f08e1627971a4cb3a0d497771fb6b7d2bcb9722Dirk Hogan prefix = new0(NDiscPrefix, 1);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (!prefix)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return -ENOMEM;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan prefix->n_ref = 1;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan LIST_INIT(prefixes, prefix);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan prefix->nd = nd;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan *ret = prefix;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan prefix = NULL;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan}
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganint sd_ndisc_set_callback(sd_ndisc *nd,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc_router_callback_t router_callback,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc_callback_t callback,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan void *userdata) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan assert(nd);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan nd->router_callback = router_callback;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->prefix_onlink_callback = prefix_onlink_callback;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->prefix_autonomous_callback = prefix_autonomous_callback;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->callback = callback;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->userdata = userdata;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan}
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hoganint sd_ndisc_set_index(sd_ndisc *nd, int interface_index) {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan assert(nd);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan assert(interface_index >= -1);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->index = interface_index;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan return 0;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan}
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hoganint sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan assert(nd);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (mac_addr)
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan else
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan zero(nd->mac_addr);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan}
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganint sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority) {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan int r;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan assert_return(nd, -EINVAL);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan assert_return(!nd->event, -EBUSY);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (event)
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->event = sd_event_ref(event);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan else {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan r = sd_event_default(&nd->event);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (r < 0)
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan return 0;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan }
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->event_priority = priority;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan return 0;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan}
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganint sd_ndisc_detach_event(sd_ndisc *nd) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan assert_return(nd, -EINVAL);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan nd->event = sd_event_unref(nd->event);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan}
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogansd_event *sd_ndisc_get_event(sd_ndisc *nd) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan assert(nd);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return nd->event;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan}
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogansd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (!nd)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return NULL;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan assert(nd->n_ref > 0);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->n_ref++;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return nd;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan}
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hoganstatic int ndisc_init(sd_ndisc *nd) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan assert(nd);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->recv = sd_event_source_unref(nd->recv);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->fd = asynchronous_close(nd->fd);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->timeout = sd_event_source_unref(nd->timeout);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return 0;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan}
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogansd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan NDiscPrefix *prefix, *p;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (!nd)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return NULL;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan assert(nd->n_ref > 0);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->n_ref--;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (nd->n_ref > 0)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return NULL;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan ndisc_init(nd);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan sd_ndisc_detach_event(nd);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan prefix = ndisc_prefix_unref(prefix);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan free(nd);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return NULL;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan}
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk HoganDEFINE_TRIVIAL_CLEANUP_FUNC(sd_ndisc*, sd_ndisc_unref);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan#define _cleanup_sd_ndisc_free_ _cleanup_(sd_ndisc_unrefp)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hoganint sd_ndisc_new(sd_ndisc **ret) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan _cleanup_sd_ndisc_free_ sd_ndisc *nd = NULL;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan assert(ret);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd = new0(sd_ndisc, 1);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (!nd)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return -ENOMEM;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->n_ref = 1;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->index = -1;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan nd->fd = -1;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan LIST_HEAD_INIT(nd->prefixes);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan *ret = nd;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan nd = NULL;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan}
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganint sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan assert_return(nd, -EINVAL);
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan assert_return(mtu, -EINVAL);
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan if (nd->mtu == 0)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return -ENOMSG;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan *mtu = nd->mtu;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan}
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hoganstatic int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan const struct in6_addr *addr,
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan uint8_t addr_prefixlen) {
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan uint8_t bytes, mask, len;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan assert_return(prefix, -EINVAL);
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan assert_return(addr, -EINVAL);
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan len = MIN(prefixlen, addr_prefixlen);
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan bytes = len / 8;
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan mask = 0xff << (8 - len % 8);
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hogan if (memcmp(prefix, addr, bytes) != 0 ||
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return -EADDRNOTAVAIL;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan}
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganstatic int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan uint8_t addr_len, NDiscPrefix **result) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan NDiscPrefix *prefix, *p;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan usec_t time_now;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan int r;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan assert(nd);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan if (r < 0)
55e32e1d339c1e3417aa96111d48d51eb29be585Dirk Hogan return r;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan if (prefix->valid_until < time_now) {
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan prefix = ndisc_prefix_unref(prefix);
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan continue;
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan }
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan *result = prefix;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return 0;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan }
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan }
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return -EADDRNOTAVAIL;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan}
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganstatic int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
1f08e1627971a4cb3a0d497771fb6b7d2bcb9722Dirk Hogan const struct nd_opt_prefix_info *prefix_opt) {
1f08e1627971a4cb3a0d497771fb6b7d2bcb9722Dirk Hogan NDiscPrefix *prefix;
1f08e1627971a4cb3a0d497771fb6b7d2bcb9722Dirk Hogan uint32_t lifetime_valid, lifetime_preferred;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan usec_t time_now;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan char time_string[FORMAT_TIMESPAN_MAX];
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan int r;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan assert(nd);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan assert(prefix_opt);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (len < prefix_opt->nd_opt_pi_len)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return -ENOMSG;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO)))
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (lifetime_valid < lifetime_preferred)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefix_opt->nd_opt_pi_prefix_len, &prefix);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (r < 0 && r != -EADDRNOTAVAIL)
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan return r;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan /* if router advertisment prefix valid timeout is zero, the timeout
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan callback will be called immediately to clean up the prefix */
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (r == -EADDRNOTAVAIL) {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan r = ndisc_prefix_new(nd, &prefix);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (r < 0)
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan return r;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefix->len = prefix_opt->nd_opt_pi_prefix_len;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan sizeof(prefix->addr));
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefix->len, lifetime_valid,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan LIST_PREPEND(prefixes, nd->prefixes, prefix);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan } else {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan uint8_t prefixlen;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefix->len,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefix_opt->nd_opt_pi_prefix_len,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan prefixlen);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefix->len = prefixlen;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan }
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefix->len, lifetime_valid,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan }
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (r < 0)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan return r;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid,
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->userdata);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return 0;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan}
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hoganstatic int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan ssize_t len) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan void *opt;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan struct nd_opt_hdr *opt_hdr;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan assert_return(nd, -EINVAL);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan assert_return(ra, -EINVAL);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan len -= sizeof(*ra);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (len < NDISC_OPT_LEN_UNITS) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan log_ndisc(nd, "Router Advertisement below minimum length");
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return -ENOMSG;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan }
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan opt = ra + 1;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan opt_hdr = opt;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan struct nd_opt_mtu *opt_mtu;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan uint32_t mtu;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan struct nd_opt_prefix_info *opt_prefix;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (opt_hdr->nd_opt_len == 0)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return -ENOMSG;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan switch (opt_hdr->nd_opt_type) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan case ND_OPT_MTU:
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan opt_mtu = opt;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (mtu != nd->mtu) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->mtu = MAX(mtu, IP6_MIN_MTU);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan log_ndisc(nd, "Router Advertisement link MTU %d using %d",
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan mtu, nd->mtu);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan }
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan break;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan case ND_OPT_PREFIX_INFORMATION:
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan opt_prefix = opt;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan ndisc_prefix_update(nd, len, opt_prefix);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan break;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan }
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan opt = (void *)((char *)opt +
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan opt_hdr = opt;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan }
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (len > 0)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan return 0;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan}
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganstatic int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan _cleanup_free_ struct nd_router_advert *ra = NULL;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc *nd = userdata;
int r, buflen = 0, pref, stateful;
union sockaddr_union router = {};
socklen_t router_len = sizeof(router);
struct in6_addr *gw;
unsigned lifetime;
ssize_t len;
assert(s);
assert(nd);
assert(nd->event);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
return -errno;
else if (buflen < 0)
/* This really should not happen */
return -EIO;
ra = malloc(buflen);
if (!ra)
return -ENOMEM;
len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len);
if (len < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
return -errno;
} else if (router_len == 0)
gw = NULL; /* only happens when running the test-suite over a socketpair */
else if (router_len != sizeof(router.in6)) {
log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
return 0;
} else
gw = &router.in6.sin6_addr;
if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
_cleanup_free_ char *addr = NULL;
(void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr);
log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr));
return 0;
}
if (ra->nd_ra_type != ND_ROUTER_ADVERT)
return 0;
if (ra->nd_ra_code != 0)
return 0;
nd->timeout = sd_event_source_unref(nd->timeout);
nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER);
pref = (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF) >> 3;
switch (pref) {
case ND_RA_FLAG_PREF_LOW:
case ND_RA_FLAG_PREF_HIGH:
break;
default:
pref = ND_RA_FLAG_PREF_MEDIUM;
break;
}
lifetime = be16toh(ra->nd_ra_router_lifetime);
log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none",
pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium",
lifetime);
r = ndisc_ra_parse(nd, ra, len);
if (r < 0) {
log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r));
return 0;
}
if (nd->router_callback)
nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
return 0;
}
static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
sd_ndisc *nd = userdata;
uint64_t time_now, next_timeout;
struct ether_addr unset = { };
struct ether_addr *addr = NULL;
int r;
assert(s);
assert(nd);
assert(nd->event);
nd->timeout = sd_event_source_unref(nd->timeout);
if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
if (nd->callback)
nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
} else {
if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
addr = &nd->mac_addr;
r = icmp6_send_router_solicitation(nd->fd, addr);
if (r < 0)
log_ndisc(nd, "Error sending Router Solicitation");
else {
nd->state = NDISC_STATE_SOLICITATION_SENT;
log_ndisc(nd, "Sent Router Solicitation");
}
nd->nd_sent++;
assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
next_timeout, 0,
ndisc_router_solicitation_timeout, nd);
if (r < 0) {
/* we cannot continue if we are unable to rearm the timer */
sd_ndisc_stop(nd);
return 0;
}
r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
if (r < 0)
return 0;
r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
if (r < 0)
return 0;
}
return 0;
}
int sd_ndisc_stop(sd_ndisc *nd) {
assert_return(nd, -EINVAL);
assert_return(nd->event, -EINVAL);
log_ndisc(client, "Stop NDisc");
ndisc_init(nd);
nd->state = NDISC_STATE_IDLE;
if (nd->callback)
nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata);
return 0;
}
int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
int r;
assert(nd);
assert(nd->event);
if (nd->state != NDISC_STATE_IDLE)
return -EBUSY;
if (nd->index < 1)
return -EINVAL;
r = icmp6_bind_router_solicitation(nd->index);
if (r < 0)
return r;
nd->fd = r;
r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
ndisc_router_advertisment_recv, nd);
if (r < 0)
goto error;
r = sd_event_source_set_priority(nd->recv, nd->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(nd->recv, "ndisc-receive-message");
if (r < 0)
goto error;
r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
0, 0, ndisc_router_solicitation_timeout, nd);
if (r < 0)
goto error;
r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
error:
if (r < 0)
ndisc_init(nd);
else
log_ndisc(client, "Start Router Solicitation");
return r;
}