summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2004-04-25 07:16:25 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2004-04-25 07:16:25 +0000
commit37c92f570b0006ae5ef608e55d28d1b71232c851 (patch)
treef74f02c9ddeef9c1db5ab08ae2446b5214bca6e5
parent02e4ba579e5d3a5e96773d05e9ea6941ea48bf83 (diff)
add "neighbor cloning", allowing you to specify a prefix and prefixlength
instead of the neighbor's IP address. WHen a connection comes in matching that mask we clone the neighbor spec. IPv6 match code by itojun, rde feeding by claudio, ok claudio
-rw-r--r--usr.sbin/bgpd/bgpd.h6
-rw-r--r--usr.sbin/bgpd/log.c21
-rw-r--r--usr.sbin/bgpd/parse.y22
-rw-r--r--usr.sbin/bgpd/rde.c38
-rw-r--r--usr.sbin/bgpd/session.c98
5 files changed, 141 insertions, 44 deletions
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h
index 800c3b9d092..48029c3b392 100644
--- a/usr.sbin/bgpd/bgpd.h
+++ b/usr.sbin/bgpd/bgpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: bgpd.h,v 1.108 2004/04/24 19:36:19 henning Exp $ */
+/* $OpenBSD: bgpd.h,v 1.109 2004/04/25 07:16:24 henning Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -150,6 +150,9 @@ struct peer_config {
char descr[PEER_DESCR_LEN];
struct bgpd_addr remote_addr;
struct bgpd_addr local_addr;
+ u_int8_t template;
+ u_int8_t remote_masklen;
+ u_int8_t cloned;
u_int32_t max_prefix;
u_int16_t remote_as;
u_int8_t ebgp; /* 1 = ebgp, 0 = ibgp */
@@ -306,6 +309,7 @@ struct session_up {
u_int32_t remote_bgpid;
struct bgpd_addr local_addr;
struct bgpd_addr remote_addr;
+ struct peer_config conf;
};
struct ctl_show_nexthop {
diff --git a/usr.sbin/bgpd/log.c b/usr.sbin/bgpd/log.c
index 3575d886fd3..ad0daf325a8 100644
--- a/usr.sbin/bgpd/log.c
+++ b/usr.sbin/bgpd/log.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.c,v 1.32 2004/04/25 01:52:11 henning Exp $ */
+/* $OpenBSD: log.c,v 1.33 2004/04/25 07:16:24 henning Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -110,17 +110,27 @@ char *
log_fmt_peer(const struct peer_config *peer)
{
const char *ip;
- char *pfmt;
+ char *pfmt, *p;
ip = log_addr(&peer->remote_addr);
+ if ((peer->remote_addr.af == AF_INET && peer->remote_masklen != 32) ||
+ (peer->remote_addr.af == AF_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)", ip, peer->descr) ==
+ if (asprintf(&pfmt, "neighbor %s (%s)", p, peer->descr) ==
-1)
fatal(NULL);
} else {
- if (asprintf(&pfmt, "neighbor %s", ip) == -1)
+ if (asprintf(&pfmt, "neighbor %s", p) == -1)
fatal(NULL);
}
+ free(p);
return (pfmt);
}
@@ -365,8 +375,7 @@ log_notification(const struct peer *peer, u_int8_t errcode, u_int8_t subcode,
void
log_conn_attempt(const struct peer *peer, struct sockaddr *sa)
{
- char *p, buf[48];
-
+ char *p, buf[NI_MAXHOST];
if (peer == NULL) { /* connection from non-peer, drop */
if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0,
diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y
index c71cbccfe96..758ae6b2269 100644
--- a/usr.sbin/bgpd/parse.y
+++ b/usr.sbin/bgpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.79 2004/04/24 19:36:19 henning Exp $ */
+/* $OpenBSD: parse.y,v 1.80 2004/04/25 07:16:24 henning Exp $ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -119,7 +119,7 @@ typedef struct {
%type <v.number> number asnumber optnumber yesno inout
%type <v.string> string
%type <v.addr> address
-%type <v.prefix> prefix
+%type <v.prefix> prefix addrspec
%type <v.u8> action quick direction
%type <v.filter_peers> filter_peer
%type <v.filter_match> filter_match prefixlenop
@@ -345,6 +345,16 @@ prefix : STRING '/' number {
}
;
+addrspec : address {
+ memcpy(&$$.prefix, &$1, sizeof(struct bgpd_addr));
+ if ($$.prefix.af == AF_INET)
+ $$.len = 32;
+ else
+ $$.len = 128;
+ }
+ | prefix
+ ;
+
optnl : '\n' optnl
|
;
@@ -357,9 +367,13 @@ optnumber : /* empty */ { $$ = 0; }
;
neighbor : { curpeer = new_peer(); }
- NEIGHBOR address optnl '{' optnl {
- memcpy(&curpeer->conf.remote_addr, &$3,
+ NEIGHBOR addrspec optnl '{' optnl {
+ memcpy(&curpeer->conf.remote_addr, &$3.prefix,
sizeof(curpeer->conf.remote_addr));
+ curpeer->conf.remote_masklen = $3.len;
+ if (($3.prefix.af == AF_INET && $3.len != 32) ||
+ ($3.prefix.af == AF_INET6 && $3.len != 128))
+ curpeer->conf.template = 1;
if (get_id(curpeer)) {
yyerror("get_id failed");
YYERROR;
diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c
index e4c02a485f9..b4231f78a88 100644
--- a/usr.sbin/bgpd/rde.c
+++ b/usr.sbin/bgpd/rde.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde.c,v 1.102 2004/04/25 02:57:55 henning Exp $ */
+/* $OpenBSD: rde.c,v 1.103 2004/04/25 07:16:24 henning Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -932,7 +932,6 @@ struct peer_table {
void
peer_init(struct peer *peer_l, u_int32_t hashsize)
{
- struct peer *p, *next;
u_int32_t hs, i;
for (hs = 1; hs < hashsize; hs <<= 1)
@@ -946,14 +945,6 @@ peer_init(struct peer *peer_l, u_int32_t hashsize)
LIST_INIT(&peerlist);
peertable.peer_hashmask = hs - 1;
-
- for (p = peer_l; p != NULL; p = next) {
- next = p->next;
- p->conf.reconf_action = RECONF_NONE;
- peer_add(p->conf.id, &p->conf);
- free(p);
- }
- peer_l = NULL;
}
void
@@ -988,7 +979,7 @@ struct rde_peer *
peer_add(u_int32_t id, struct peer_config *p_conf)
{
struct rde_peer_head *head;
- struct rde_peer *peer;
+ struct rde_peer *peer;
ENSURE(peer_get(id) == NULL);
@@ -1014,22 +1005,14 @@ peer_add(u_int32_t id, struct peer_config *p_conf)
void
peer_remove(struct rde_peer *peer)
{
- /*
- * If the session is up we wait until we get the IMSG_SESSION_DOWN
- * message. If the session is down or was never up we delete the
- * peer.
- */
- if (peer->state == PEER_UP) {
- peer->conf.reconf_action = RECONF_DELETE;
- } else {
- ENSURE(peer_get(peer->conf.id) != NULL);
- ENSURE(LIST_EMPTY(&peer->path_h));
+ ENSURE(peer->state == PEER_DOWN);
+ ENSURE(peer_get(peer->conf.id) != NULL);
+ ENSURE(LIST_EMPTY(&peer->path_h));
- LIST_REMOVE(peer, hash_l);
- LIST_REMOVE(peer, peer_l);
+ LIST_REMOVE(peer, hash_l);
+ LIST_REMOVE(peer, peer_l);
- free(peer);
- }
+ free(peer);
}
void
@@ -1037,7 +1020,7 @@ peer_up(u_int32_t id, struct session_up *sup)
{
struct rde_peer *peer;
- peer = peer_get(id);
+ peer = peer_add(id, &sup->conf);
if (peer == NULL) {
log_warnx("peer_up: unknown peer id %d", id);
return;
@@ -1083,8 +1066,7 @@ peer_down(u_int32_t id)
}
LIST_INIT(&peer->path_h);
- if (peer->conf.reconf_action == RECONF_DELETE)
- peer_remove(peer);
+ peer_remove(peer);
}
/*
diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c
index a3321d6aeb8..aac6cc01fa6 100644
--- a/usr.sbin/bgpd/session.c
+++ b/usr.sbin/bgpd/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.144 2004/04/24 20:15:49 henning Exp $ */
+/* $OpenBSD: session.c,v 1.145 2004/04/25 07:16:24 henning Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -81,6 +81,7 @@ void session_up(struct peer *);
void session_down(struct peer *);
struct peer *getpeerbyip(struct sockaddr *);
+int session_match_mask(struct peer *, struct sockaddr *);
struct peer *getpeerbyid(u_int32_t);
static struct sockaddr *addr2sa(struct bgpd_addr *, u_int16_t);
@@ -455,7 +456,7 @@ bgp_fsm(struct peer *peer, enum session_events event)
return;
}
- if (peer->conf.passive) {
+ if (peer->conf.passive || peer->conf.template) {
change_state(peer, STATE_ACTIVE, event);
peer->ConnectRetryTimer = 0;
} else {
@@ -722,12 +723,14 @@ change_state(struct peer *peer, enum session_state state,
pfkey_auth_remove(peer);
if (peer->state == STATE_ESTABLISHED)
session_down(peer);
- if (event != EVNT_STOP) {
+ if (event != EVNT_STOP && !peer->conf.cloned) {
peer->IdleHoldTimer = time(NULL) + peer->IdleHoldTime;
if (event != EVNT_NONE &&
peer->IdleHoldTime < MAX_IDLE_HOLD/2)
peer->IdleHoldTime *= 2;
}
+ if (peer->state != STATE_NONE && peer->conf.cloned)
+ peer->conf.reconf_action = RECONF_DELETE;
break;
case STATE_CONNECT:
break;
@@ -1876,11 +1879,13 @@ getpeerbyaddr(struct bgpd_addr *addr)
struct peer *
getpeerbyip(struct sockaddr *ip)
{
- struct peer *p;
+ struct peer *p, *newpeer, *loose = NULL;
+ u_int32_t id;
/* we might want a more effective way to find peers by IP */
for (p = peers; p != NULL; p = p->next)
- if (p->conf.remote_addr.af == ip->sa_family) {
+ if (!p->conf.template &&
+ p->conf.remote_addr.af == ip->sa_family) {
if (p->conf.remote_addr.af == AF_INET &&
p->conf.remote_addr.v4.s_addr ==
((struct sockaddr_in *)ip)->sin_addr.s_addr)
@@ -1892,9 +1897,91 @@ getpeerbyip(struct sockaddr *ip)
return (p);
}
+ /* try template matching */
+ for (p = peers; p != NULL; p = p->next)
+ if (p->conf.template &&
+ p->conf.remote_addr.af == ip->sa_family &&
+ session_match_mask(p, ip))
+ if (loose == NULL || loose->conf.remote_masklen <
+ p->conf.remote_masklen)
+ loose = p;
+
+ if (loose != NULL) {
+ /* clone */
+ if ((newpeer = malloc(sizeof(struct peer))) == NULL)
+ fatal(NULL);
+ memcpy(newpeer, loose, sizeof(struct peer));
+ for (id = 1; id < UINT_MAX; id++) {
+ for (p = peers; p != NULL && p->conf.id != id;
+ p = p->next)
+ ; /* nothing */
+ if (p == NULL) { /* we found a free id */
+ newpeer->conf.id = id;
+ break;
+ }
+ }
+ if (newpeer->conf.remote_addr.af == AF_INET) {
+ newpeer->conf.remote_addr.v4.s_addr =
+ ((struct sockaddr_in *)ip)->sin_addr.s_addr;
+ newpeer->conf.remote_masklen = 32;
+ }
+ if (newpeer->conf.remote_addr.af == AF_INET6) {
+ memcpy(&p->conf.remote_addr.v6,
+ &((struct sockaddr_in6 *)ip)->sin6_addr,
+ sizeof(newpeer->conf.remote_addr.v6));
+ newpeer->conf.remote_masklen = 128;
+ }
+ newpeer->conf.template = 0;
+ newpeer->conf.cloned = 1;
+ newpeer->state = STATE_NONE;
+ newpeer->rbuf = NULL;
+ init_peer(newpeer);
+ bgp_fsm(newpeer, EVNT_START);
+ newpeer->next = peers;
+ peers = newpeer;
+ return (newpeer);
+ }
+
return (NULL);
}
+int
+session_match_mask(struct peer *p, struct sockaddr *ip)
+{
+ int i;
+ in_addr_t v4mask;
+ struct in6_addr *in;
+ struct in6_addr mask;
+
+ if (p->conf.remote_addr.af == AF_INET) {
+ v4mask = htonl(0xffffffff << (32 - p->conf.remote_masklen));
+ if (p->conf.remote_addr.v4.s_addr ==
+ ((((struct sockaddr_in *)ip)->sin_addr.s_addr) & v4mask))
+ return (1);
+ else
+ return (0);
+ }
+
+ if (p->conf.remote_addr.af == AF_INET6) {
+ for (i = 0; i < p->conf.remote_masklen / 8; i++)
+ mask.s6_addr[i] = 0xff;
+ i = p->conf.remote_masklen % 8;
+ if (i)
+ mask.s6_addr[p->conf.remote_masklen / 8] = 0xff00 >> i;
+
+ in = &((struct sockaddr_in6 *)ip)->sin6_addr;
+
+ for (i = 0; i < 16; i++)
+ if ((in->s6_addr[i] & mask.s6_addr[i]) !=
+ p->conf.remote_addr.addr8[i])
+ return (0);
+
+ return (1);
+ }
+
+ return (0);
+}
+
struct peer *
getpeerbyid(u_int32_t peerid)
{
@@ -1949,6 +2036,7 @@ session_up(struct peer *peer)
fatalx("session_up: unsupported address family");
}
+ memcpy(&sup.conf, &peer->conf, sizeof(sup.conf));
peer->stats.last_updown = time(NULL);
if (imsg_compose(&ibuf_rde, IMSG_SESSION_UP, peer->conf.id,
&sup, sizeof(sup)) == -1)