#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syslog.h>
#include "bgpd.h"
#include "session.h"
#include "log.h"
char *
log_fmt_peer(const struct peer_config *peer)
{
const char *ip;
char *pfmt, *p;
ip = log_addr(&peer->remote_addr);
if ((peer->remote_addr.aid == AID_INET && peer->remote_masklen != 32) ||
(peer->remote_addr.aid == AID_INET6 &&
peer->remote_masklen != 128)) {
if (asprintf(&p, "%s/%u", ip, peer->remote_masklen) == -1)
fatal(NULL);
} else {
if ((p = strdup(ip)) == NULL)
fatal(NULL);
}
if (peer->descr[0]) {
if (asprintf(&pfmt, "neighbor %s (%s)", p, peer->descr) ==
-1)
fatal(NULL);
} else {
if (asprintf(&pfmt, "neighbor %s", p) == -1)
fatal(NULL);
}
free(p);
return (pfmt);
}
void
log_peer_info(const struct peer_config *peer, const char *emsg, ...)
{
char *p, *msg;
va_list ap;
p = log_fmt_peer(peer);
va_start(ap, emsg);
if (vasprintf(&msg, emsg, ap) == -1)
fatal(NULL);
va_end(ap);
logit(LOG_INFO, "%s: %s", p, msg);
free(msg);
free(p);
}
void
log_peer_warn(const struct peer_config *peer, const char *emsg, ...)
{
char *p, *msg;
va_list ap;
int saved_errno = errno;
p = log_fmt_peer(peer);
if (emsg == NULL) {
logit(LOG_ERR, "%s: %s", p, strerror(saved_errno));
} else {
va_start(ap, emsg);
if (vasprintf(&msg, emsg, ap) == -1)
fatal(NULL);
va_end(ap);
logit(LOG_ERR, "%s: %s: %s", p, msg, strerror(saved_errno));
free(msg);
}
free(p);
}
void
log_peer_warnx(const struct peer_config *peer, const char *emsg, ...)
{
char *p, *msg;
va_list ap;
p = log_fmt_peer(peer);
va_start(ap, emsg);
if (vasprintf(&msg, emsg, ap) == -1)
fatal(NULL);
va_end(ap);
logit(LOG_ERR, "%s: %s", p, msg);
free(msg);
free(p);
}
void
log_statechange(struct peer *peer, enum session_state ostate,
enum session_events event)
{
if (peer->state == STATE_CONNECT && peer->prev_state == STATE_ACTIVE &&
ostate == STATE_CONNECT)
return;
if (peer->state == STATE_ACTIVE && peer->prev_state == STATE_CONNECT &&
ostate == STATE_ACTIVE)
return;
peer->lasterr = 0;
log_peer_info(&peer->conf, "state change %s -> %s, reason: %s",
statenames[peer->prev_state], statenames[peer->state],
eventnames[event]);
}
static const char *
tohex(const unsigned char *in, size_t len)
{
const char hex[] = "0123456789ABCDEF";
static char out[(16 + 1) * 3];
size_t i, o = 0;
if (len == 0)
return "";
if (len > 16)
len = 16;
for (i = 0; i < len; i++) {
out[o++] = hex[in[i] >> 4];
out[o++] = hex[in[i] & 0xf];
out[o++] = ' ';
if (i == 7)
out[o++] = ' ';
}
out[o - 1] = '\0';
return out;
}
void
log_notification(const struct peer *peer, uint8_t errcode, uint8_t subcode,
const struct ibuf *data, const char *dir)
{
struct ibuf ibuf;
char *p;
const char *suberrname = NULL;
int uk = 0, dump = 0;
if (data != NULL)
ibuf_from_ibuf(&ibuf, data);
else
ibuf_from_buffer(&ibuf, NULL, 0);
p = log_fmt_peer(&peer->conf);
switch (errcode) {
case ERR_HEADER:
if (subcode >= nitems(suberr_header_names) ||
suberr_header_names[subcode] == NULL)
uk = 1;
else
suberrname = suberr_header_names[subcode];
break;
case ERR_OPEN:
if (subcode >= nitems(suberr_open_names) ||
suberr_open_names[subcode] == NULL)
uk = 1;
else
suberrname = suberr_open_names[subcode];
if (errcode == ERR_OPEN && subcode == ERR_OPEN_CAPA) {
uint8_t capa_code;
if (ibuf_get_n8(&ibuf, &capa_code) == -1)
break;
logit(LOG_ERR, "%s: %s notification: %s, %s: %s",
p, dir, errnames[errcode], suberrname,
log_capability(capa_code));
free(p);
return;
}
break;
case ERR_UPDATE:
if (subcode >= nitems(suberr_update_names) ||
suberr_update_names[subcode] == NULL)
uk = 1;
else
suberrname = suberr_update_names[subcode];
dump = 1;
break;
case ERR_CEASE:
if (subcode >= nitems(suberr_cease_names) ||
suberr_cease_names[subcode] == NULL)
uk = 1;
else
suberrname = suberr_cease_names[subcode];
if (subcode == ERR_CEASE_ADMIN_DOWN ||
subcode == ERR_CEASE_ADMIN_RESET) {
uint8_t len;
if (ibuf_get_n8(&ibuf, &len) != -1 && len != 0) {
char *s;
if ((s = ibuf_get_string(&ibuf, len)) != NULL) {
logit(LOG_ERR, "%s: %s notification: "
"%s, %s: reason \"%s\"", p, dir,
errnames[errcode], suberrname,
log_reason(s));
free(s);
free(p);
return;
}
}
}
break;
case ERR_HOLDTIMEREXPIRED:
if (subcode != 0)
uk = 1;
break;
case ERR_FSM:
if (subcode >= nitems(suberr_fsm_names) ||
suberr_fsm_names[subcode] == NULL)
uk = 1;
else
suberrname = suberr_fsm_names[subcode];
break;
case ERR_RREFRESH:
if (subcode >= nitems(suberr_rrefresh_names) ||
suberr_rrefresh_names[subcode] == NULL)
uk = 1;
else
suberrname = suberr_rrefresh_names[subcode];
break;
default:
logit(LOG_ERR, "%s: %s notification, unknown errcode "
"%u, subcode %u", p, dir, errcode, subcode);
free(p);
return;
}
if (uk)
logit(LOG_ERR, "%s: %s notification: %s, unknown subcode %u",
p, dir, errnames[errcode], subcode);
else {
if (suberrname == NULL)
logit(LOG_ERR, "%s: %s notification: %s", p,
dir, errnames[errcode]);
else
logit(LOG_ERR, "%s: %s notification: %s, %s",
p, dir, errnames[errcode], suberrname);
}
if (dump && log_getverbose() && ibuf_size(&ibuf) > 0) {
size_t off = 0;
logit(LOG_INFO, "%s: notification data", p);
while (ibuf_size(&ibuf) > 0) {
unsigned char buf[16];
size_t len = sizeof(buf);
if (ibuf_size(&ibuf) < len)
len = ibuf_size(&ibuf);
if (ibuf_get(&ibuf, buf, len) == -1)
break;
logit(LOG_INFO, " %5zu: %s", off, tohex(buf, len));
off += len;
}
}
free(p);
}
void
log_conn_attempt(const struct peer *peer, struct sockaddr *sa, socklen_t len)
{
char *p;
if (peer == NULL) {
if (log_getverbose())
logit(LOG_INFO, "connection from non-peer %s refused",
log_sockaddr(sa, len));
} else {
if (peer->conf.down && peer->state == STATE_IDLE)
return;
p = log_fmt_peer(&peer->conf);
logit(LOG_INFO, "Connection attempt from %s while session is "
"in state %s", p, statenames[peer->state]);
free(p);
}
}