SecBSD 1.3 -current: The Digital Resistance. A belief in the importance of privacy and the right to secure communication.
This commit is contained in:
commit
95114fbda2
91022 changed files with 33913249 additions and 0 deletions
531
usr.sbin/ldpd/interface.c
Normal file
531
usr.sbin/ldpd/interface.c
Normal file
|
@ -0,0 +1,531 @@
|
|||
/* $OpenBSD: interface.c,v 1.52 2023/03/08 04:43:13 guenther Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
|
||||
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ldpd.h"
|
||||
#include "ldpe.h"
|
||||
#include "log.h"
|
||||
|
||||
static struct if_addr *if_addr_new(struct kaddr *);
|
||||
static struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *);
|
||||
static int if_start(struct iface *, int);
|
||||
static int if_reset(struct iface *, int);
|
||||
static void if_update_af(struct iface_af *, int);
|
||||
static void if_hello_timer(int, short, void *);
|
||||
static void if_start_hello_timer(struct iface_af *);
|
||||
static void if_stop_hello_timer(struct iface_af *);
|
||||
static int if_join_ipv4_group(struct iface *, struct in_addr *);
|
||||
static int if_leave_ipv4_group(struct iface *, struct in_addr *);
|
||||
static int if_join_ipv6_group(struct iface *, struct in6_addr *);
|
||||
static int if_leave_ipv6_group(struct iface *, struct in6_addr *);
|
||||
|
||||
struct iface *
|
||||
if_new(struct kif *kif)
|
||||
{
|
||||
struct iface *iface;
|
||||
|
||||
if ((iface = calloc(1, sizeof(*iface))) == NULL)
|
||||
fatal("if_new: calloc");
|
||||
|
||||
strlcpy(iface->name, kif->ifname, sizeof(iface->name));
|
||||
|
||||
/* get type */
|
||||
if (kif->flags & IFF_POINTOPOINT)
|
||||
iface->type = IF_TYPE_POINTOPOINT;
|
||||
if (kif->flags & IFF_BROADCAST &&
|
||||
kif->flags & IFF_MULTICAST)
|
||||
iface->type = IF_TYPE_BROADCAST;
|
||||
|
||||
/* get index and flags */
|
||||
LIST_INIT(&iface->addr_list);
|
||||
iface->ifindex = kif->ifindex;
|
||||
iface->rdomain = kif->rdomain;
|
||||
iface->flags = kif->flags;
|
||||
iface->linkstate = kif->link_state;
|
||||
iface->if_type = kif->if_type;
|
||||
|
||||
/* ipv4 */
|
||||
iface->ipv4.af = AF_INET;
|
||||
iface->ipv4.iface = iface;
|
||||
iface->ipv4.enabled = 0;
|
||||
iface->ipv4.state = IF_STA_DOWN;
|
||||
LIST_INIT(&iface->ipv4.adj_list);
|
||||
|
||||
/* ipv6 */
|
||||
iface->ipv6.af = AF_INET6;
|
||||
iface->ipv6.iface = iface;
|
||||
iface->ipv6.enabled = 0;
|
||||
iface->ipv6.state = IF_STA_DOWN;
|
||||
LIST_INIT(&iface->ipv6.adj_list);
|
||||
|
||||
return (iface);
|
||||
}
|
||||
|
||||
void
|
||||
if_exit(struct iface *iface)
|
||||
{
|
||||
struct if_addr *if_addr;
|
||||
|
||||
log_debug("%s: interface %s", __func__, iface->name);
|
||||
|
||||
if (iface->ipv4.state == IF_STA_ACTIVE)
|
||||
if_reset(iface, AF_INET);
|
||||
if (iface->ipv6.state == IF_STA_ACTIVE)
|
||||
if_reset(iface, AF_INET6);
|
||||
|
||||
while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) {
|
||||
LIST_REMOVE(if_addr, entry);
|
||||
free(if_addr);
|
||||
}
|
||||
}
|
||||
|
||||
struct iface *
|
||||
if_lookup(struct ldpd_conf *xconf, unsigned short ifindex)
|
||||
{
|
||||
struct iface *iface;
|
||||
|
||||
LIST_FOREACH(iface, &xconf->iface_list, entry)
|
||||
if (iface->ifindex == ifindex)
|
||||
return (iface);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct iface_af *
|
||||
iface_af_get(struct iface *iface, int af)
|
||||
{
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
return (&iface->ipv4);
|
||||
case AF_INET6:
|
||||
return (&iface->ipv6);
|
||||
default:
|
||||
fatalx("iface_af_get: unknown af");
|
||||
}
|
||||
}
|
||||
|
||||
static struct if_addr *
|
||||
if_addr_new(struct kaddr *ka)
|
||||
{
|
||||
struct if_addr *if_addr;
|
||||
|
||||
if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
|
||||
fatal(__func__);
|
||||
|
||||
if_addr->af = ka->af;
|
||||
if_addr->addr = ka->addr;
|
||||
if_addr->prefixlen = ka->prefixlen;
|
||||
if_addr->dstbrd = ka->dstbrd;
|
||||
|
||||
return (if_addr);
|
||||
}
|
||||
|
||||
static struct if_addr *
|
||||
if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka)
|
||||
{
|
||||
struct if_addr *if_addr;
|
||||
int af = ka->af;
|
||||
|
||||
LIST_FOREACH(if_addr, addr_list, entry)
|
||||
if (!ldp_addrcmp(af, &if_addr->addr, &ka->addr) &&
|
||||
if_addr->prefixlen == ka->prefixlen &&
|
||||
!ldp_addrcmp(af, &if_addr->dstbrd, &ka->dstbrd))
|
||||
return (if_addr);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
if_addr_add(struct kaddr *ka)
|
||||
{
|
||||
struct iface *iface;
|
||||
struct if_addr *if_addr;
|
||||
struct nbr *nbr;
|
||||
|
||||
if (if_addr_lookup(&global.addr_list, ka) == NULL) {
|
||||
if_addr = if_addr_new(ka);
|
||||
|
||||
LIST_INSERT_HEAD(&global.addr_list, if_addr, entry);
|
||||
RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
|
||||
if (nbr->state != NBR_STA_OPER)
|
||||
continue;
|
||||
if (if_addr->af == AF_INET && !nbr->v4_enabled)
|
||||
continue;
|
||||
if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
|
||||
continue;
|
||||
|
||||
send_address_single(nbr, if_addr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
iface = if_lookup(leconf, ka->ifindex);
|
||||
if (iface) {
|
||||
if (ka->af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6))
|
||||
iface->linklocal = ka->addr.v6;
|
||||
|
||||
if (if_addr_lookup(&iface->addr_list, ka) == NULL) {
|
||||
if_addr = if_addr_new(ka);
|
||||
LIST_INSERT_HEAD(&iface->addr_list, if_addr, entry);
|
||||
if_update(iface, if_addr->af);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
if_addr_del(struct kaddr *ka)
|
||||
{
|
||||
struct iface *iface;
|
||||
struct if_addr *if_addr;
|
||||
struct nbr *nbr;
|
||||
|
||||
iface = if_lookup(leconf, ka->ifindex);
|
||||
if (iface) {
|
||||
if (ka->af == AF_INET6 &&
|
||||
IN6_ARE_ADDR_EQUAL(&iface->linklocal, &ka->addr.v6))
|
||||
memset(&iface->linklocal, 0, sizeof(iface->linklocal));
|
||||
|
||||
if_addr = if_addr_lookup(&iface->addr_list, ka);
|
||||
if (if_addr) {
|
||||
LIST_REMOVE(if_addr, entry);
|
||||
if_update(iface, if_addr->af);
|
||||
free(if_addr);
|
||||
}
|
||||
}
|
||||
|
||||
if_addr = if_addr_lookup(&global.addr_list, ka);
|
||||
if (if_addr) {
|
||||
RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
|
||||
if (nbr->state != NBR_STA_OPER)
|
||||
continue;
|
||||
if (if_addr->af == AF_INET && !nbr->v4_enabled)
|
||||
continue;
|
||||
if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
|
||||
continue;
|
||||
send_address_single(nbr, if_addr, 1);
|
||||
}
|
||||
LIST_REMOVE(if_addr, entry);
|
||||
free(if_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
if_start(struct iface *iface, int af)
|
||||
{
|
||||
struct iface_af *ia;
|
||||
struct timeval now;
|
||||
|
||||
log_debug("%s: %s address-family %s", __func__, iface->name,
|
||||
af_name(af));
|
||||
|
||||
ia = iface_af_get(iface, af);
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
ia->uptime = now.tv_sec;
|
||||
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
if (if_join_ipv4_group(iface, &global.mcast_addr_v4))
|
||||
return (-1);
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (if_join_ipv6_group(iface, &global.mcast_addr_v6))
|
||||
return (-1);
|
||||
break;
|
||||
default:
|
||||
fatalx("if_start: unknown af");
|
||||
}
|
||||
|
||||
send_hello(HELLO_LINK, ia, NULL);
|
||||
|
||||
evtimer_set(&ia->hello_timer, if_hello_timer, ia);
|
||||
if_start_hello_timer(ia);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
if_reset(struct iface *iface, int af)
|
||||
{
|
||||
struct iface_af *ia;
|
||||
struct adj *adj;
|
||||
|
||||
log_debug("%s: %s address-family %s", __func__, iface->name,
|
||||
af_name(af));
|
||||
|
||||
ia = iface_af_get(iface, af);
|
||||
if_stop_hello_timer(ia);
|
||||
|
||||
while ((adj = LIST_FIRST(&ia->adj_list)) != NULL)
|
||||
adj_del(adj, S_SHUTDOWN);
|
||||
|
||||
/* try to cleanup */
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
if (global.ipv4.ldp_disc_socket != -1)
|
||||
if_leave_ipv4_group(iface, &global.mcast_addr_v4);
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (global.ipv6.ldp_disc_socket != -1)
|
||||
if_leave_ipv6_group(iface, &global.mcast_addr_v6);
|
||||
break;
|
||||
default:
|
||||
fatalx("if_start: unknown af");
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
if_update_af(struct iface_af *ia, int link_ok)
|
||||
{
|
||||
int addr_ok = 0, socket_ok, rtr_id_ok;
|
||||
struct if_addr *if_addr;
|
||||
|
||||
switch (ia->af) {
|
||||
case AF_INET:
|
||||
/*
|
||||
* NOTE: for LDPv4, each interface should have at least one
|
||||
* valid IP address otherwise they can not be enabled.
|
||||
*/
|
||||
LIST_FOREACH(if_addr, &ia->iface->addr_list, entry) {
|
||||
if (if_addr->af == AF_INET) {
|
||||
addr_ok = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
/* for IPv6 the link-local address is enough. */
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&ia->iface->linklocal))
|
||||
addr_ok = 1;
|
||||
break;
|
||||
default:
|
||||
fatalx("if_update_af: unknown af");
|
||||
}
|
||||
|
||||
if ((ldp_af_global_get(&global, ia->af))->ldp_disc_socket != -1)
|
||||
socket_ok = 1;
|
||||
else
|
||||
socket_ok = 0;
|
||||
|
||||
if (leconf->rtr_id.s_addr != INADDR_ANY)
|
||||
rtr_id_ok = 1;
|
||||
else
|
||||
rtr_id_ok = 0;
|
||||
|
||||
if (ia->state == IF_STA_DOWN) {
|
||||
if (!ia->enabled || !link_ok || !addr_ok || !socket_ok ||
|
||||
!rtr_id_ok)
|
||||
return;
|
||||
|
||||
ia->state = IF_STA_ACTIVE;
|
||||
if_start(ia->iface, ia->af);
|
||||
} else if (ia->state == IF_STA_ACTIVE) {
|
||||
if (ia->enabled && link_ok && addr_ok && socket_ok && rtr_id_ok)
|
||||
return;
|
||||
|
||||
ia->state = IF_STA_DOWN;
|
||||
if_reset(ia->iface, ia->af);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
if_update(struct iface *iface, int af)
|
||||
{
|
||||
int link_ok;
|
||||
|
||||
link_ok = (iface->flags & IFF_UP) &&
|
||||
LINK_STATE_IS_UP(iface->linkstate);
|
||||
|
||||
if (af == AF_INET || af == AF_UNSPEC)
|
||||
if_update_af(&iface->ipv4, link_ok);
|
||||
if (af == AF_INET6 || af == AF_UNSPEC)
|
||||
if_update_af(&iface->ipv6, link_ok);
|
||||
}
|
||||
|
||||
void
|
||||
if_update_all(int af)
|
||||
{
|
||||
struct iface *iface;
|
||||
|
||||
LIST_FOREACH(iface, &leconf->iface_list, entry)
|
||||
if_update(iface, af);
|
||||
}
|
||||
|
||||
/* timers */
|
||||
static void
|
||||
if_hello_timer(int fd, short event, void *arg)
|
||||
{
|
||||
struct iface_af *ia = arg;
|
||||
|
||||
send_hello(HELLO_LINK, ia, NULL);
|
||||
if_start_hello_timer(ia);
|
||||
}
|
||||
|
||||
static void
|
||||
if_start_hello_timer(struct iface_af *ia)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = ia->hello_interval;
|
||||
if (evtimer_add(&ia->hello_timer, &tv) == -1)
|
||||
fatal(__func__);
|
||||
}
|
||||
|
||||
static void
|
||||
if_stop_hello_timer(struct iface_af *ia)
|
||||
{
|
||||
if (evtimer_pending(&ia->hello_timer, NULL) &&
|
||||
evtimer_del(&ia->hello_timer) == -1)
|
||||
fatal(__func__);
|
||||
}
|
||||
|
||||
struct ctl_iface *
|
||||
if_to_ctl(struct iface_af *ia)
|
||||
{
|
||||
static struct ctl_iface ictl;
|
||||
struct timeval now;
|
||||
struct adj *adj;
|
||||
|
||||
ictl.af = ia->af;
|
||||
memcpy(ictl.name, ia->iface->name, sizeof(ictl.name));
|
||||
ictl.ifindex = ia->iface->ifindex;
|
||||
ictl.state = ia->state;
|
||||
ictl.flags = ia->iface->flags;
|
||||
ictl.linkstate = ia->iface->linkstate;
|
||||
ictl.type = ia->iface->type;
|
||||
ictl.if_type = ia->iface->if_type;
|
||||
ictl.hello_holdtime = ia->hello_holdtime;
|
||||
ictl.hello_interval = ia->hello_interval;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
if (ia->state != IF_STA_DOWN &&
|
||||
ia->uptime != 0) {
|
||||
ictl.uptime = now.tv_sec - ia->uptime;
|
||||
} else
|
||||
ictl.uptime = 0;
|
||||
|
||||
ictl.adj_cnt = 0;
|
||||
LIST_FOREACH(adj, &ia->adj_list, ia_entry)
|
||||
ictl.adj_cnt++;
|
||||
|
||||
return (&ictl);
|
||||
}
|
||||
|
||||
/* multicast membership sockopts */
|
||||
in_addr_t
|
||||
if_get_ipv4_addr(struct iface *iface)
|
||||
{
|
||||
struct if_addr *if_addr;
|
||||
|
||||
LIST_FOREACH(if_addr, &iface->addr_list, entry)
|
||||
if (if_addr->af == AF_INET)
|
||||
return (if_addr->addr.v4.s_addr);
|
||||
|
||||
return (INADDR_ANY);
|
||||
}
|
||||
|
||||
static int
|
||||
if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
|
||||
{
|
||||
struct ip_mreq mreq;
|
||||
|
||||
log_debug("%s: interface %s addr %s", __func__, iface->name,
|
||||
inet_ntoa(*addr));
|
||||
|
||||
mreq.imr_multiaddr = *addr;
|
||||
mreq.imr_interface.s_addr = if_get_ipv4_addr(iface);
|
||||
|
||||
if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP,
|
||||
IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) == -1) {
|
||||
log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s",
|
||||
__func__, iface->name, inet_ntoa(*addr));
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
|
||||
{
|
||||
struct ip_mreq mreq;
|
||||
|
||||
log_debug("%s: interface %s addr %s", __func__, iface->name,
|
||||
inet_ntoa(*addr));
|
||||
|
||||
mreq.imr_multiaddr = *addr;
|
||||
mreq.imr_interface.s_addr = if_get_ipv4_addr(iface);
|
||||
|
||||
if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP,
|
||||
IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) == -1) {
|
||||
log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
|
||||
"address %s", __func__, iface->name, inet_ntoa(*addr));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
if_join_ipv6_group(struct iface *iface, struct in6_addr *addr)
|
||||
{
|
||||
struct ipv6_mreq mreq;
|
||||
|
||||
log_debug("%s: interface %s addr %s", __func__, iface->name,
|
||||
log_in6addr(addr));
|
||||
|
||||
mreq.ipv6mr_multiaddr = *addr;
|
||||
mreq.ipv6mr_interface = iface->ifindex;
|
||||
|
||||
if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
|
||||
IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) {
|
||||
log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s",
|
||||
__func__, iface->name, log_in6addr(addr));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
|
||||
{
|
||||
struct ipv6_mreq mreq;
|
||||
|
||||
log_debug("%s: interface %s addr %s", __func__, iface->name,
|
||||
log_in6addr(addr));
|
||||
|
||||
mreq.ipv6mr_multiaddr = *addr;
|
||||
mreq.ipv6mr_interface = iface->ifindex;
|
||||
|
||||
if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
|
||||
IPV6_LEAVE_GROUP, (void *)&mreq, sizeof(mreq)) == -1) {
|
||||
log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s",
|
||||
__func__, iface->name, log_in6addr(addr));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue