File: [local] / src / usr.sbin / dvmrpd / kroute.c (download)
Revision 1.14, Mon Jul 24 11:00:01 2017 UTC (6 years, 10 months ago) by friehm
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, OPENBSD_6_3_BASE, OPENBSD_6_3, OPENBSD_6_2_BASE, OPENBSD_6_2, HEAD Changes since 1.13: +4 -4 lines
Unify ROUNDUP macros for parsing route messages.
Use the macro from route(8) / ospf6d(8) since it works also with argument 0.
OK claudio@
|
/* $OpenBSD: kroute.c,v 1.14 2017/07/24 11:00:01 friehm Exp $ */
/*
* Copyright (c) 2004 Esben Norby <norby@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@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/socket.h>
#include <sys/sysctl.h>
#include <sys/tree.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dvmrp.h"
#include "dvmrpd.h"
#include "log.h"
struct {
u_int32_t rtseq;
pid_t pid;
struct event ev;
} kr_state;
struct kif_node {
RB_ENTRY(kif_node) entry;
struct kif k;
};
int kif_compare(struct kif_node *, struct kif_node *);
struct kif_node *kif_find(int);
int kif_insert(struct kif_node *);
int kif_remove(struct kif_node *);
void kif_clear(void);
in_addr_t prefixlen2mask(u_int8_t);
void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
void if_change(u_short, int, struct if_data *);
void if_announce(void *);
int fetchifs(int);
RB_HEAD(kif_tree, kif_node) kit;
RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
int
kif_init(void)
{
RB_INIT(&kit);
if (fetchifs(0) == -1)
return (-1);
return (0);
}
int
kr_init(void)
{
int opt, fd;
if ((fd = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
0)) == -1) {
log_warn("kr_init: socket");
return (-1);
}
/* not interested in my own messages */
if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, sizeof(opt)) == -1)
log_warn("kr_init: setsockopt"); /* not fatal */
kr_state.pid = getpid();
kr_state.rtseq = 1;
event_set(&kr_state.ev, fd, EV_READ | EV_PERSIST,
kr_dispatch_msg, NULL);
event_add(&kr_state.ev, NULL);
return (0);
}
void
kr_shutdown(void)
{
kif_clear();
close(EVENT_FD((&kr_state.ev)));
}
void
kr_ifinfo(char *ifname)
{
struct kif_node *kif;
RB_FOREACH(kif, kif_tree, &kit)
if (!strcmp(ifname, kif->k.ifname)) {
main_imsg_compose_dvmrpe(IMSG_CTL_IFINFO, 0,
&kif->k, sizeof(kif->k));
return;
}
}
/* rb-tree compare */
int
kif_compare(struct kif_node *a, struct kif_node *b)
{
return (b->k.ifindex - a->k.ifindex);
}
struct kif_node *
kif_find(int ifindex)
{
struct kif_node s;
memset(&s, 0, sizeof(s));
s.k.ifindex = ifindex;
return (RB_FIND(kif_tree, &kit, &s));
}
struct kif *
kif_findname(char *ifname)
{
struct kif_node *kif;
RB_FOREACH(kif, kif_tree, &kit)
if (!strcmp(ifname, kif->k.ifname))
return (&kif->k);
return (NULL);
}
int
kif_insert(struct kif_node *kif)
{
if (RB_INSERT(kif_tree, &kit, kif) != NULL) {
log_warnx("RB_INSERT(kif_tree, &kit, kif)");
free(kif);
return (-1);
}
return (0);
}
int
kif_remove(struct kif_node *kif)
{
if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
return (-1);
}
free(kif);
return (0);
}
void
kif_clear(void)
{
struct kif_node *kif;
while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
kif_remove(kif);
}
/* misc */
u_int8_t
prefixlen_classful(in_addr_t ina)
{
/* it hurt to write this. */
if (ina >= 0xf0000000U) /* class E */
return (32);
else if (ina >= 0xe0000000U) /* class D */
return (4);
else if (ina >= 0xc0000000U) /* class C */
return (24);
else if (ina >= 0x80000000U) /* class B */
return (16);
else /* class A */
return (8);
}
u_int8_t
mask2prefixlen(in_addr_t ina)
{
if (ina == 0)
return (0);
else
return (33 - ffs(ntohl(ina)));
}
in_addr_t
prefixlen2mask(u_int8_t prefixlen)
{
if (prefixlen == 0)
return (0);
return (0xffffffff << (32 - prefixlen));
}
void
if_change(u_short ifindex, int flags, struct if_data *ifd)
{
struct kif_node *kif;
u_int8_t reachable;
if ((kif = kif_find(ifindex)) == NULL) {
log_warnx("interface with index %u not found",
ifindex);
return;
}
kif->k.flags = flags;
kif->k.link_state = ifd->ifi_link_state;
kif->k.if_type = ifd->ifi_type;
kif->k.baudrate = ifd->ifi_baudrate;
if ((reachable = (flags & IFF_UP) &&
LINK_STATE_IS_UP(ifd->ifi_link_state)) == kif->k.nh_reachable)
return; /* nothing changed wrt nexthop validity */
kif->k.nh_reachable = reachable;
main_imsg_compose_dvmrpe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k));
}
void
if_announce(void *msg)
{
struct if_announcemsghdr *ifan;
struct kif_node *kif;
ifan = msg;
switch (ifan->ifan_what) {
case IFAN_ARRIVAL:
if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
log_warn("if_announce");
return;
}
kif->k.ifindex = ifan->ifan_index;
strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname));
kif_insert(kif);
break;
case IFAN_DEPARTURE:
kif = kif_find(ifan->ifan_index);
kif_remove(kif);
break;
}
}
/* rtsock */
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
void
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
{
int i;
for (i = 0; i < RTAX_MAX; i++) {
if (addrs & (1 << i)) {
rti_info[i] = sa;
sa = (struct sockaddr *)((char *)(sa) +
ROUNDUP(sa->sa_len));
} else
rti_info[i] = NULL;
}
}
int
fetchifs(int ifindex)
{
size_t len;
int mib[6];
char *buf, *next, *lim;
struct if_msghdr ifm;
struct kif_node *kif;
struct sockaddr *sa, *rti_info[RTAX_MAX];
struct sockaddr_dl *sdl;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_IFLIST;
mib[5] = ifindex;
if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
log_warn("sysctl");
return (-1);
}
if ((buf = malloc(len)) == NULL) {
log_warn("fetchif");
return (-1);
}
if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
log_warn("sysctl");
free(buf);
return (-1);
}
lim = buf + len;
for (next = buf; next < lim; next += ifm.ifm_msglen) {
memcpy(&ifm, next, sizeof(ifm));
sa = (struct sockaddr *)(next + sizeof(ifm));
get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
if (ifm.ifm_version != RTM_VERSION)
continue;
if (ifm.ifm_type != RTM_IFINFO)
continue;
if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
log_warn("fetchifs");
free(buf);
return (-1);
}
kif->k.ifindex = ifm.ifm_index;
kif->k.flags = ifm.ifm_flags;
kif->k.link_state = ifm.ifm_data.ifi_link_state;
kif->k.if_type = ifm.ifm_data.ifi_type;
kif->k.baudrate = ifm.ifm_data.ifi_baudrate;
kif->k.mtu = ifm.ifm_data.ifi_mtu;
kif->k.nh_reachable = (kif->k.flags & IFF_UP) &&
LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state);
if ((sa = rti_info[RTAX_IFP]) != NULL)
if (sa->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_nlen >= sizeof(kif->k.ifname))
memcpy(kif->k.ifname, sdl->sdl_data,
sizeof(kif->k.ifname) - 1);
else if (sdl->sdl_nlen > 0)
memcpy(kif->k.ifname, sdl->sdl_data,
sdl->sdl_nlen);
/* string already terminated via calloc() */
}
kif_insert(kif);
}
free(buf);
return (0);
}
void
kr_dispatch_msg(int fd, short event, void *bula)
{
char buf[RT_BUF_SIZE];
ssize_t n;
char *next, *lim;
struct rt_msghdr *rtm;
struct if_msghdr ifm;
if ((n = read(fd, &buf, sizeof(buf))) == -1) {
if (errno == EAGAIN || errno == EINTR)
return;
fatal("dispatch_rtmsg: read error");
}
if (n == 0)
fatalx("routing socket closed");
lim = buf + n;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)next;
if (lim < next + sizeof(u_short) ||
lim < next + rtm->rtm_msglen)
fatalx("dispatch_rtmsg: partial rtm in buffer");
if (rtm->rtm_version != RTM_VERSION)
continue;
switch (rtm->rtm_type) {
case RTM_IFINFO:
memcpy(&ifm, next, sizeof(ifm));
if_change(ifm.ifm_index, ifm.ifm_flags,
&ifm.ifm_data);
break;
case RTM_IFANNOUNCE:
if_announce(next);
break;
default:
/* ignore for now */
break;
}
}
}