diff options
author | Henning Brauer <henning@cvs.openbsd.org> | 2004-04-25 07:16:25 +0000 |
---|---|---|
committer | Henning Brauer <henning@cvs.openbsd.org> | 2004-04-25 07:16:25 +0000 |
commit | 37c92f570b0006ae5ef608e55d28d1b71232c851 (patch) | |
tree | f74f02c9ddeef9c1db5ab08ae2446b5214bca6e5 | |
parent | 02e4ba579e5d3a5e96773d05e9ea6941ea48bf83 (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.h | 6 | ||||
-rw-r--r-- | usr.sbin/bgpd/log.c | 21 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 22 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 38 | ||||
-rw-r--r-- | usr.sbin/bgpd/session.c | 98 |
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) |