Commit a38a9386 authored by Cédric Le Ninivin's avatar Cédric Le Ninivin Committed by Julien Muchembled

New -X option to control babeld via a unix socket

TODO: documentation

To be pushed upstream.
Co-authored-by: Julien Muchembled's avatarJulien Muchembled <jm@nexedi.com>
parent 916d3d9a
......@@ -11,11 +11,11 @@ LDLIBS = -lrt
SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \
route.c xroute.c message.c resend.c configuration.c local.c \
hmac.c rfc6234/sha224-256.c BLAKE2/ref/blake2s-ref.c
ctl.c hmac.c rfc6234/sha224-256.c BLAKE2/ref/blake2s-ref.c
OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \
route.o xroute.o message.o resend.o configuration.o local.o \
hmac.o rfc6234/sha224-256.o BLAKE2/ref/blake2s-ref.o
ctl.o hmac.o rfc6234/sha224-256.o BLAKE2/ref/blake2s-ref.o
babeld: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS)
......
......@@ -53,6 +53,7 @@ THE SOFTWARE.
#include "configuration.h"
#include "local.h"
#include "version.h"
#include "ctl.h"
struct timeval now;
......@@ -136,6 +137,7 @@ main(int argc, char **argv)
void *vrc;
unsigned int seed;
struct interface *ifp;
int ctl_sockfd;
gettime(&now);
......@@ -157,7 +159,7 @@ main(int argc, char **argv)
while(1) {
opt = getopt(argc, argv,
"m:p:h:H:i:k:A:srS:d:g:G:lwz:M:t:T:c:C:DL:I:V");
"m:p:h:H:i:k:A:srS:d:g:G:lwz:M:t:T:c:C:DL:I:X:V");
if(opt < 0)
break;
......@@ -302,6 +304,9 @@ main(int argc, char **argv)
fprintf(stderr, "%s\n", BABELD_VERSION);
exit(0);
break;
case 'X':
control_socket_path = optarg;
break;
default:
goto usage;
}
......@@ -521,6 +526,12 @@ main(int argc, char **argv)
}
}
ctl_sockfd = init_control_socket();
if(ctl_sockfd < 0) {
perror("Couldn't create control socket");
goto fail;
}
init_signals();
rc = resize_receive_buffer(1500);
if(rc < 0)
......@@ -574,8 +585,9 @@ main(int argc, char **argv)
while(1) {
struct timeval tv;
fd_set readfds;
struct neighbour *neigh;
fd_set readfds, writefds;
struct ctl *ctl, *ctl_next;
gettime(&now);
......@@ -597,6 +609,7 @@ main(int argc, char **argv)
timeval_min(&tv, &neigh->buf.timeout);
}
FD_ZERO(&readfds);
FD_ZERO(&writefds);
if(timeval_compare(&tv, &now) > 0) {
int maxfd = 0;
timeval_minus(&tv, &tv, &now);
......@@ -616,7 +629,16 @@ main(int argc, char **argv)
FD_SET(local_sockets[i].fd, &readfds);
maxfd = MAX(maxfd, local_sockets[i].fd);
}
rc = select(maxfd + 1, &readfds, NULL, NULL, &tv);
FD_SET(ctl_sockfd, &readfds);
maxfd = MAX(maxfd, ctl_sockfd);
for(ctl = ctl_connection; ctl; ctl = ctl->next) {
if(ctl->write < ctl->buffer_out.end)
FD_SET(ctl->fd, &writefds);
else
FD_SET(ctl->fd, &readfds);
maxfd = MAX(maxfd, ctl->fd);
}
rc = select(maxfd + 1, &readfds, &writefds, NULL, &tv);
if(rc < 0) {
if(errno != EINTR) {
perror("select");
......@@ -684,6 +706,16 @@ main(int argc, char **argv)
i++;
}
if(FD_ISSET(ctl_sockfd, &readfds)) {
accept_ctl_connection(ctl_sockfd);
}
for(ctl = ctl_connection; ctl; ctl = ctl_next) {
ctl_next = ctl->next;
if (FD_ISSET(ctl->fd, &writefds))
ctl_write(ctl);
if (FD_ISSET(ctl->fd, &readfds))
ctl_read(ctl);
}
if(reopening) {
kernel_dump_time = now.tv_sec;
check_neighbours_timeout = now;
......@@ -851,7 +883,7 @@ main(int argc, char **argv)
" "
"[-t table] [-T table] [-c file] [-C statement]\n"
" "
"[-d level] [-D] [-L logfile] [-I pidfile]\n"
"[-d level] [-D] [-L logfile] [-I pidfile] [-X control-socket]\n"
" "
"interface...\n",
BABELD_VERSION);
......
/*
Copyright (c) 2014 Nexedi SA
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include "babeld.h"
#include "util.h"
#include "interface.h"
#include "source.h"
#include "neighbour.h"
#include "kernel.h"
#include "route.h"
#include "xroute.h"
#include "ctl.h"
#define MEMCPY(y,x) (memcpy(y, x, sizeof y))
const char *control_socket_path = "/var/run/babeld.socket";
struct ctl *ctl_connection;
static void
ctl_close_connection(struct ctl *ctl)
{
struct ctl **c = &ctl_connection;
while(*c != ctl)
c = &(*c)->next;
*c = ctl->next;
close(ctl->fd);
free(ctl->buffer_in.data);
free(ctl->buffer_out.data);
free(ctl);
}
static int
ctl_resize_buffer(struct ctl_buffer *buffer, size_t n)
{
if(buffer->size < n) {
char *new;
n = (n + 4095) & ~4095;
new = realloc(buffer->data, n);
if(!new) {
fprintf(stderr, "realloc(ctl_buffer)\n");
return -1;
}
buffer->data = new;
buffer->size = n;
}
return 0;
}
static int
ctl_dump_interface(struct ctl_buffer *buffer, struct interface *ifp)
{
size_t end = buffer->end + strlen(ifp->name) + 1 + 4;
if(ctl_resize_buffer(buffer, end))
return -1;
DO_HTONL(buffer->data + buffer->end, ifp->ifindex);
strcpy(buffer->data + buffer->end + 4, ifp->name);
buffer->end = end;
return 0;
}
static int
ctl_dump_neighbour(struct ctl_buffer *buffer, struct neighbour *neigh)
{
struct ctl_dump_neighbour *neigh_dump;
size_t end = buffer->end + sizeof *neigh_dump;
if(ctl_resize_buffer(buffer, end))
return -1;
neigh_dump = (void *)(buffer->data + buffer->end);
MEMCPY(neigh_dump->address, neigh->address);
neigh_dump->ifindex = htonl(neigh->ifp->ifindex);
neigh_dump->reach = htons(neigh->hello.reach);
neigh_dump->rxcost = htons(neighbour_rxcost(neigh));
neigh_dump->txcost = htons(neigh->txcost);
neigh_dump->rtt = htonl(neigh->rtt);
neigh_dump->rttcost = htonl(neighbour_rttcost(neigh));
neigh_dump->channel = htonl(neigh->ifp->channel);
neigh_dump->if_up = htons(if_up(neigh->ifp));
buffer->end = end;
return 0;
}
static int
ctl_dump_route(struct ctl_buffer *buffer, struct babel_route *route)
{
struct ctl_dump_route *route_dump;
size_t end = buffer->end + sizeof *route_dump;
if(ctl_resize_buffer(buffer, end))
return -1;
route_dump = (void *)(buffer->data + buffer->end);
MEMCPY(route_dump->prefix, route->src->prefix);
route_dump->plen = route->src->plen;
route_dump->metric = htons(route_metric(route));
route_dump->smoothed_metric = htons(route_smoothed_metric(route));
route_dump->refmetric = htons(route->refmetric);
MEMCPY(route_dump->id, route->src->id);
route_dump->seqno = htonl((int32_t)route->seqno);
route_dump->age = htonl((int32_t)(now.tv_sec - route->time));
route_dump->ifindex = htonl(route->neigh->ifp->ifindex);
MEMCPY(route_dump->neigh_address, route->neigh->address);
MEMCPY(route_dump->nexthop, route->nexthop);
route_dump->flags = (route->installed ? CTL_ROUTE_INSTALLED : 0)
| (route_feasible(route) ? CTL_ROUTE_FEASIBLE : 0);
buffer->end = end;
return 0;
}
static int
ctl_dump_xroute(struct ctl_buffer *buffer, struct xroute *xroute)
{
struct ctl_dump_xroute *xroute_dump;
size_t end = buffer->end + sizeof *xroute_dump;
if(ctl_resize_buffer(buffer, end))
return -1;
xroute_dump = (void *)(buffer->data + buffer->end);
MEMCPY(xroute_dump->prefix, xroute->prefix);
xroute_dump->plen = xroute->plen;
xroute_dump->metric = htons(xroute->metric);
buffer->end = end;
return 0;
}
static int
ctl_dump(struct ctl_buffer *buffer, void *packet, size_t length)
{
size_t count_offset, header_offset;
unsigned count;
uint8_t flags = *(uint8_t*)packet;
uint8_t flags_routes = CTL_DUMP_ROUTE(flags);
header_offset = buffer->end;
buffer->end += 6;
#define START_ARRAY (count_offset = buffer->end += 2, count = 0)
#define END_ARRAY do { \
if(!count && ctl_resize_buffer(buffer, count_offset)) \
return -1; \
DO_HTONS(buffer->data + count_offset - 2, count); \
} while(0)
START_ARRAY;
if(flags & CTL_DUMP_INTERFACE) {
struct interface *ifp;
FOR_ALL_INTERFACES(ifp) {
/* Apparently we get some garbage interfaces from command line. */
if(!ifp->ifindex)
continue;
if(ctl_dump_interface(buffer, ifp) ||
!(uint16_t)++count /* make sure we don't overflow */)
return -1;
}
}
END_ARRAY;
START_ARRAY;
if(flags & CTL_DUMP_NEIGHBOUR) {
struct neighbour *neigh;
FOR_ALL_NEIGHBOURS(neigh)
if(ctl_dump_neighbour(buffer, neigh) || !(uint16_t)++count)
return -1;
}
END_ARRAY;
START_ARRAY;
if(flags & CTL_DUMP_XROUTE) {
struct xroute_stream *xroutes;
struct xroute *xroute;
xroutes = xroute_stream();
if(!xroutes)
return -1;
while((xroute = xroute_stream_next(xroutes)) &&
!ctl_dump_xroute(buffer, xroute) &&
(uint16_t)++count);
xroute_stream_done(xroutes);
if(xroute)
return -1;
}
END_ARRAY;
START_ARRAY;
if(flags_routes != CTL_DUMP_ROUTE_NONE) {
struct babel_route *route;
struct route_stream *routes;
routes = route_stream(flags_routes == CTL_DUMP_ROUTE_INSTALLED);
if(!routes)
return -1;
while((route = route_stream_next(routes)) &&
((flags_routes == CTL_DUMP_ROUTE_FEASIBLE && !route_feasible(route))
|| !ctl_dump_route(buffer, route)) &&
(uint16_t)++count);
route_stream_done(routes);
if(route)
return -1;
}
END_ARRAY;
#undef START_ARRAY
#undef END_ARRAY
DO_HTONS(buffer->data + header_offset, CTL_MSG_DUMP);
DO_HTONL(buffer->data + header_offset + 2, buffer->end - header_offset - 6);
return 0;
}
static void
ctl_work(struct ctl *ctl)
{
struct ctl_buffer *buffer_in = &ctl->buffer_in;
uint16_t type;
uint32_t length;
size_t packet_size;
while((packet_size = sizeof type + sizeof length) <= buffer_in->end) {
void *p = buffer_in->data;
int ret = -1;
DO_NTOHS(type, p); p += sizeof type;
DO_NTOHL(length, p); p += sizeof length;
packet_size += length;
if(packet_size <= buffer_in->end)
switch(type) {
case CTL_MSG_DUMP:
ret = ctl_dump(&ctl->buffer_out, p, length);
break;
}
else if(length < CTL_MAX_SIZE)
return;
if(ret)
return ctl_close_connection(ctl);
else
memmove(buffer_in->data, p + length,
buffer_in->end -= packet_size);
}
}
static void
unlink_control_socket()
{
unlink(control_socket_path);
}
int
init_control_socket()
{
struct sockaddr_un sa_un;
struct stat stat;
int fd;
if(strlen(control_socket_path) >= sizeof sa_un.sun_path)
return -1;
if(!lstat(control_socket_path, &stat)) {
if(!S_ISSOCK(stat.st_mode))
return -1;
unlink(control_socket_path);
}
if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -1;
sa_un.sun_family = AF_UNIX;
strcpy(sa_un.sun_path, control_socket_path);
if(bind(fd, (struct sockaddr *)&sa_un, sizeof sa_un) < 0) {
close(fd);
return -1;
}
atexit(unlink_control_socket);
listen(fd, 5);
return fd;
}
void
accept_ctl_connection(int fd)
{
struct ctl *ctl;
fd = accept(fd, NULL, NULL);
if(fd < 0)
return;
ctl = calloc(1, sizeof *ctl);
if(!ctl) {
close(fd);
return;
}
ctl->fd = fd;
ctl->next = ctl_connection;
ctl_connection = ctl;
}
void
ctl_read(struct ctl *ctl)
{
struct ctl_buffer *buffer = &ctl->buffer_in;
size_t end = buffer->end;
size_t read_n = buffer->size - end;
if(read_n || (ctl_resize_buffer(buffer, buffer->size + 1),
read_n = buffer->size - end)) {
ssize_t n = recv(ctl->fd, buffer->data + buffer->end, read_n, 0);
if(n > 0) {
buffer->end = end + n;
if(!ctl->initialized) {
if(buffer->end &&
(ctl->initialized = *(uint8_t*)buffer->data == CTL_VERSION))
memmove(buffer->data, buffer->data + 1, --buffer->end);
else
return ctl_close_connection(ctl);
}
return ctl_work(ctl);
}
if(n && errno == EINTR)
return;
}
ctl_close_connection(ctl);
}
void
ctl_write(struct ctl *ctl)
{
struct ctl_buffer *buffer = &ctl->buffer_out;
ssize_t n;
n = send(ctl->fd, buffer->data + ctl->write, buffer->end - ctl->write, 0);
if(n > 0) {
ctl->write += n;
if(ctl->write == buffer->end)
ctl->write = buffer->end = 0;
} else if(!n || errno != EINTR)
ctl_close_connection(ctl);
}
/*
Copyright (c) 2014 Nexedi SA
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdint.h>
/* Soft limit to protect against misbehaviour,
* but feel free to increase it if needed. */
#define CTL_MAX_SIZE 65536
#define CTL_VERSION 1
#define CTL_MSG_DUMP 1
#define CTL_DUMP_INTERFACE (1 << 0)
#define CTL_DUMP_NEIGHBOUR (1 << 1)
#define CTL_DUMP_XROUTE (1 << 2)
#define CTL_DUMP_ROUTE(x) (x >> 3 & 3)
#define CTL_DUMP_ROUTE_NONE 0
#define CTL_DUMP_ROUTE_INSTALLED 1
#define CTL_DUMP_ROUTE_FEASIBLE 2
#define CTL_DUMP_ROUTE_ALL 3
#define CTL_ROUTE_INSTALLED (1 << 0)
#define CTL_ROUTE_FEASIBLE (1 << 1)
struct ctl_buffer {
char *data;
size_t size;
size_t end;
};
struct ctl_dump_neighbour {
uint8_t address[16];
uint32_t ifindex;
uint16_t reach;
uint16_t rxcost;
uint16_t txcost;
uint16_t rtt;
uint16_t rttcost;
int32_t channel;
uint16_t if_up;
} __attribute__((packed));
struct ctl_dump_route {
uint8_t prefix[16];
uint8_t plen;
uint16_t metric;
uint16_t smoothed_metric;
uint16_t refmetric;
uint8_t id[8];
int32_t seqno;
int32_t age;
uint32_t ifindex;
uint8_t neigh_address[16];
uint8_t nexthop[16];
uint8_t flags;
} __attribute__((packed));
struct ctl_dump_xroute {
uint8_t prefix[16];
uint8_t plen;
uint16_t metric;
} __attribute__((packed));
struct ctl {
struct ctl *next;
int fd;
struct ctl_buffer buffer_out;
struct ctl_buffer buffer_in;
size_t write;
unsigned initialized:1;
};
extern const char *control_socket_path;
extern struct ctl *ctl_connection;
int init_control_socket();
void accept_ctl_connection(int fd);
void ctl_read(struct ctl *ctl);
void ctl_write(struct ctl *ctl);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment