diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/ospfd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/ospfd/ospf.h | 18 | ||||
-rw-r--r-- | usr.sbin/ospfd/ospfd.c | 6 | ||||
-rw-r--r-- | usr.sbin/ospfd/ospfd.conf.5 | 24 | ||||
-rw-r--r-- | usr.sbin/ospfd/ospfd.h | 74 | ||||
-rw-r--r-- | usr.sbin/ospfd/ospfe.c | 10 | ||||
-rw-r--r-- | usr.sbin/ospfd/parse.y | 30 | ||||
-rw-r--r-- | usr.sbin/ospfd/rde.c | 26 | ||||
-rw-r--r-- | usr.sbin/ospfd/rde.h | 41 | ||||
-rw-r--r-- | usr.sbin/ospfd/rde_lsdb.c | 38 | ||||
-rw-r--r-- | usr.sbin/ospfd/rde_spf.c | 715 |
11 files changed, 932 insertions, 54 deletions
diff --git a/usr.sbin/ospfd/Makefile b/usr.sbin/ospfd/Makefile index fc5ff114ec6..bc34712525d 100644 --- a/usr.sbin/ospfd/Makefile +++ b/usr.sbin/ospfd/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.2 2005/02/02 21:22:28 norby Exp $ +# $OpenBSD: Makefile,v 1.3 2005/02/27 08:21:15 norby Exp $ PROG= ospfd SRCS= area.c auth.c buffer.c config.c control.c database.c hello.c \ imsg.c in_cksum.c interface.c iso_cksum.c kroute.c lsack.c \ lsreq.c lsupdate.c log.c neighbor.c ospfd.c ospfe.c packet.c \ - parse.y rde.c rde_lsdb.c + parse.y rde.c rde_lsdb.c rde_spf.c MAN= ospfd.8 ospfd.conf.5 diff --git a/usr.sbin/ospfd/ospf.h b/usr.sbin/ospfd/ospf.h index 5cd333ffd71..40570137ef6 100644 --- a/usr.sbin/ospfd/ospf.h +++ b/usr.sbin/ospfd/ospf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ospf.h,v 1.7 2005/02/08 21:52:48 norby Exp $ */ +/* $OpenBSD: ospf.h,v 1.8 2005/02/27 08:21:15 norby Exp $ */ /* * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> @@ -60,6 +60,14 @@ #define DEFAULT_NBR_TMOUT 86400 /* 24 hours */ +#define DEFAULT_SPF_DELAY 1 +#define MIN_SPF_DELAY 1 +#define MAX_SPF_DELAY 10 + +#define DEFAULT_SPF_HOLDTIME 5 +#define MIN_SPF_HOLDTIME 1 +#define MAX_SPF_HOLDTIME 5 + /* OSPF compatibility flags */ #define OSPF_OPTION_E 0x02 #define OSPF_OPTION_MC 0x04 @@ -160,6 +168,10 @@ struct ls_upd_hdr { #define LSA_METRIC_MASK 0x00ffffff /* only for sum & as-ext */ #define LSA_ASEXT_E_FLAG 0x80000000 +#define OSPF_RTR_B 0x01 +#define OSPF_RTR_E 0x02 +#define OSPF_RTR_V 0x04 + struct lsa_rtr { u_int8_t flags; u_int8_t dummy; @@ -179,6 +191,10 @@ struct lsa_net { u_int32_t att_rtr[1]; }; +struct lsa_net_link { + u_int32_t att_rtr; +}; + struct lsa_sum { u_int32_t mask; u_int32_t metric; /* only lower 24 bit */ diff --git a/usr.sbin/ospfd/ospfd.c b/usr.sbin/ospfd/ospfd.c index 44549f1386b..165f859ee27 100644 --- a/usr.sbin/ospfd/ospfd.c +++ b/usr.sbin/ospfd/ospfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ospfd.c,v 1.5 2005/02/24 16:28:43 claudio Exp $ */ +/* $OpenBSD: ospfd.c,v 1.6 2005/02/27 08:21:15 norby Exp $ */ /* * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> @@ -367,6 +367,10 @@ main_dispatch_rde(int fd, short event, void *bula) break; switch (imsg.hdr.type) { + case IMSG_KROUTE_CHANGE: + if (kr_change(imsg.data)) + log_warn("main_dispatch_rde: error changing route"); + break; default: log_debug("main_dispatch_rde: error handling imsg %d", imsg.hdr.type); diff --git a/usr.sbin/ospfd/ospfd.conf.5 b/usr.sbin/ospfd/ospfd.conf.5 index 0e7913c974b..b0329e12726 100644 --- a/usr.sbin/ospfd/ospfd.conf.5 +++ b/usr.sbin/ospfd/ospfd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ospfd.conf.5,v 1.4 2005/02/07 05:51:00 david Exp $ +.\" $OpenBSD: ospfd.conf.5,v 1.5 2005/02/27 08:21:15 norby Exp $ .\" .\" Copyright (c) 2005 Esben Norby <norby@openbsd.org> .\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -89,17 +89,17 @@ The default is .It Ic router-id Ar address Set the router ID; if not specified, the lowest IP address of the router will be used. -.\".It Ic spf-delay Ar seconds -.\"Set SPF delay in seconds. -.\"The delay between receiving an update to the link -.\"state database and starting the shortest path first calculation. -.\"The default value is 1; valid range is 1\-10 seconds. -.\".Pp -.\".It Ic spf-holdtime Ar seconds -.\"Set the SPF holdtime in seconds. -.\"The minimum time between two consecutive -.\"shortest path first calculations. -.\"The default value is 5 seconds; the valid is range 1\-5 seconds. +.It Ic spf-delay Ar seconds +Set SPF delay in seconds. +The delay between receiving an update to the link +state database and starting the shortest path first calculation. +The default value is 1; valid range is 1\-10 seconds. +.Pp +.It Ic spf-holdtime Ar seconds +Set the SPF holdtime in seconds. +The minimum time between two consecutive +shortest path first calculations. +The default value is 5 seconds; the valid is range 1\-5 seconds. .El .Sh AREAS Areas are used for grouping interfaces. diff --git a/usr.sbin/ospfd/ospfd.h b/usr.sbin/ospfd/ospfd.h index fafaf3e8e2f..1c338dcd448 100644 --- a/usr.sbin/ospfd/ospfd.h +++ b/usr.sbin/ospfd/ospfd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ospfd.h,v 1.10 2005/02/24 16:28:43 claudio Exp $ */ +/* $OpenBSD: ospfd.h,v 1.11 2005/02/27 08:21:15 norby Exp $ */ /* * Copyright (c) 2004 Esben Norby <norby@openbsd.org> @@ -29,9 +29,9 @@ #include <event.h> #include <stdbool.h> -#define CONF_FILE "/etc/ospfd.conf" -#define OSPFD_SOCKET "/var/run/ospfd.sock" -#define OSPFD_USER "_ospfd" +#define CONF_FILE "/etc/ospfd.conf" +#define OSPFD_SOCKET "/var/run/ospfd.sock" +#define OSPFD_USER "_ospfd" #define NBR_HASHSIZE 128 #define LSA_HASHSIZE 512 @@ -95,6 +95,8 @@ enum imsg_type { IMSG_CTL_FIB_DECOUPLE, IMSG_CTL_AREA, IMSG_CTL_END, + IMSG_KROUTE_CHANGE, + IMSG_KROUTE_DELETE, IMSG_IFINFO, IMSG_NEIGHBOR_UP, IMSG_NEIGHBOR_DOWN, @@ -134,24 +136,24 @@ struct rde_nbr; RB_HEAD(lsa_tree, vertex); struct area { - LIST_ENTRY(area) entry; - struct in_addr id; - - struct lsa_tree lsa_tree; - LIST_HEAD(, iface) iface_list; - LIST_HEAD(, rde_nbr) nbr_list; -/* list addr_range_list; */ - u_int32_t stub_default_cost; - - u_int32_t dead_interval; - u_int16_t transfer_delay; - u_int16_t hello_interval; - u_int16_t rxmt_interval; - u_int16_t metric; - u_int8_t priority; - - bool transit; - bool stub; + LIST_ENTRY(area) entry; + struct in_addr id; + + struct lsa_tree lsa_tree; + LIST_HEAD(, iface) iface_list; + LIST_HEAD(, rde_nbr) nbr_list; +/* list addr_range_list; */ + u_int32_t stub_default_cost; + + u_int32_t dead_interval; + u_int16_t transfer_delay; + u_int16_t hello_interval; + u_int16_t rxmt_interval; + u_int16_t metric; + u_int8_t priority; + + bool transit; + bool stub; }; /* interface states */ @@ -213,6 +215,28 @@ enum auth_type { AUTH_CRYPT }; +/* spf states */ +enum spf_state { + SPF_IDLE, + SPF_DELAY, + SPF_HOLD, + SPF_HOLDQUEUE +}; + +enum path_type { + PT_INTRA_AREA, + PT_INTER_AREA, + PT_TYPE1_EXT, + PT_TYPE2_EXT +}; + +static const char * const path_type_names[] = { + "Intra-Area", + "Inter-Area", + "Type 1 external", + "Type 2 external" +}; + /* lsa list used in RDE and OE */ TAILQ_HEAD(lsa_head, lsa_entry); @@ -265,6 +289,7 @@ enum { struct ospfd_conf { struct event ev; + struct event spf_timer; struct in_addr rtr_id; u_int32_t opts; #define OSPFD_OPT_VERBOSE 0x00000001 @@ -272,8 +297,11 @@ struct ospfd_conf { #define OSPFD_OPT_NOACTION 0x00000004 int maxdepth; LIST_HEAD(, area) area_list; - + LIST_HEAD(, vertex) cand_list; struct lsa_tree lsa_tree; + u_int32_t spf_delay; + u_int32_t spf_hold_time; + int spf_state; int ospf_socket; int flags; int options; /* OSPF options */ diff --git a/usr.sbin/ospfd/ospfe.c b/usr.sbin/ospfd/ospfe.c index 77f92bfbdd6..a1ba93f5311 100644 --- a/usr.sbin/ospfd/ospfe.c +++ b/usr.sbin/ospfd/ospfe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ospfe.c,v 1.7 2005/02/19 10:19:56 norby Exp $ */ +/* $OpenBSD: ospfe.c,v 1.8 2005/02/27 08:21:15 norby Exp $ */ /* * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> @@ -340,7 +340,7 @@ ospfe_dispatch_rde(int fd, short event, void *bula) if (nbr == NULL) fatalx("ospfe_dispatch_rde: " "neighbor not found"); - + nbr->dd_pending--; if (nbr->dd_pending == 0 && nbr->state & NBR_STA_LOAD) { if (ls_req_list_empty(nbr)) @@ -660,7 +660,7 @@ orig_rtr_lsa(struct area *area) num_links++; if (buf_add(buf, &rtr_link, sizeof(rtr_link))) fatalx("orig_rtr_lsa: buf_add failed"); - + LIST_FOREACH(nbr, &iface->nbr_list, entry) { if (nbr != iface->self && nbr->state & NBR_STA_FULL) { @@ -692,7 +692,7 @@ orig_rtr_lsa(struct area *area) } /* LSA router header */ - lsa_rtr.flags = 0; /* XXX */ + lsa_rtr.flags = oeconf->flags; /* XXX */ lsa_rtr.dummy = 0; lsa_rtr.nlinks = htons(num_links); memcpy(buf_seek(buf, sizeof(lsa_hdr), sizeof(lsa_rtr)), @@ -712,7 +712,7 @@ orig_rtr_lsa(struct area *area) chksum = htons(iso_cksum(buf->buf, buf->wpos, LS_CKSUM_OFFSET)); memcpy(buf_seek(buf, LS_CKSUM_OFFSET, sizeof(chksum)), &chksum, sizeof(chksum)); - + if (self) imsg_compose(ibuf_rde, IMSG_LS_UPD, self->peerid, 0, -1, buf->buf, buf->wpos); diff --git a/usr.sbin/ospfd/parse.y b/usr.sbin/ospfd/parse.y index fb37b166b74..fca2dd01d5f 100644 --- a/usr.sbin/ospfd/parse.y +++ b/usr.sbin/ospfd/parse.y @@ -1,7 +1,7 @@ -/* $OpenBSD: parse.y,v 1.4 2005/02/02 21:02:25 norby Exp $ */ +/* $OpenBSD: parse.y,v 1.5 2005/02/27 08:21:15 norby Exp $ */ /* - * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -98,6 +98,7 @@ typedef struct { %} %token AREA INTERFACE ROUTERID FIBUPDATE +%token SPFDELAY SPFHOLDTIME %token AUTHKEY AUTHTYPE %token METRIC PASSIVE %token HELLOINTERVAL TRANSMITDELAY @@ -229,6 +230,24 @@ conf_main : METRIC number { else conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE; } + | SPFDELAY number { + if ($2 < MIN_SPF_DELAY || $2 > MAX_SPF_DELAY) { + yyerror("spf-delay out of range " + "(%d-%d)", MIN_SPF_DELAY, + MAX_SPF_DELAY); + YYERROR; + } + conf->spf_delay = $2; + } + | SPFHOLDTIME number { + if ($2 < MIN_SPF_HOLDTIME || $2 > MAX_SPF_HOLDTIME) { + yyerror("spf-holdtime out of range " + "(%d-%d)", MIN_SPF_HOLDTIME, + MAX_SPF_HOLDTIME); + YYERROR; + } + conf->spf_hold_time = $2; + } ; authtype : AUTHTYPE STRING { @@ -466,7 +485,9 @@ lookup(char *s) {"router-dead-time", ROUTERDEADTIME}, {"router-id", ROUTERID}, {"router-priority", ROUTERPRIORITY}, - {"transmit-delay", TRANSMITDELAY}, + {"spf-delay", SPFDELAY}, + {"spf-holdtime", SPFHOLDTIME}, + {"transmit-delay", TRANSMITDELAY} }; const struct keywords *p; @@ -689,6 +710,9 @@ parse_config(char *filename, int opts) defaults.priority = DEFAULT_PRIORITY; conf->options = OSPF_OPTION_E; + conf->spf_delay = DEFAULT_SPF_DELAY; + conf->spf_hold_time = DEFAULT_SPF_HOLDTIME; + conf->spf_state = SPF_IDLE; if ((fin = fopen(filename, "r")) == NULL) { warn("%s", filename); diff --git a/usr.sbin/ospfd/rde.c b/usr.sbin/ospfd/rde.c index a0125141205..5d79486695c 100644 --- a/usr.sbin/ospfd/rde.c +++ b/usr.sbin/ospfd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.6 2005/02/10 14:05:48 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.7 2005/02/27 08:21:15 norby Exp $ */ /* * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> @@ -142,6 +142,10 @@ rde(struct ospfd_conf *xconf, int pipe_parent2rde[2], int pipe_ospfe2rde[2], ibuf_main->handler, ibuf_main); event_add(&ibuf_main->ev, NULL); + evtimer_set(&rdeconf->spf_timer, spf_timer, rdeconf); + cand_list_init(); + rt_init(); + event_dispatch(); rde_shutdown(); @@ -153,8 +157,8 @@ rde(struct ospfd_conf *xconf, int pipe_parent2rde[2], int pipe_ospfe2rde[2], void rde_shutdown(void) { - - /* ... */ + stop_spf_timer(rdeconf); + cand_list_clr(); msgbuf_write(&ibuf_ospfe->w); msgbuf_clear(&ibuf_ospfe->w); @@ -356,6 +360,7 @@ rde_dispatch_imsg(int fd, short event, void *bula) if (nbr->self) { lsa_merge(nbr, lsa, v); + start_spf_timer(rdeconf); break; } @@ -379,6 +384,10 @@ rde_dispatch_imsg(int fd, short event, void *bula) v->nbr->peerid, 0, -1, v->lsa, ntohs(v->lsa->hdr.len)); /* TODO LSA on req list -> BadLSReq */ + + /* start spf_timer */ + log_debug("rde_dispatch_imsg: start spf_timer"); + start_spf_timer(rdeconf); } else if (r < 0) { /* new LSA older than DB */ if (ntohl(db_hdr->seq_num) == MAX_SEQ_NUM && @@ -463,7 +472,18 @@ rde_router_id(void) return (rdeconf->rtr_id.s_addr); } +void +rde_send_kroute(struct rt_node *r) +{ + struct kroute kr; + + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = r->prefix.s_addr; + kr.nexthop.s_addr = r->nexthop.s_addr; + kr.prefixlen = r->prefixlen; + imsg_compose(ibuf_main, IMSG_KROUTE_CHANGE, 0, 0, -1, &kr, sizeof(kr)); +} LIST_HEAD(rde_nbr_head, rde_nbr); diff --git a/usr.sbin/ospfd/rde.h b/usr.sbin/ospfd/rde.h index 80e70023491..72ca6893b2a 100644 --- a/usr.sbin/ospfd/rde.h +++ b/usr.sbin/ospfd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.4 2005/02/09 22:58:08 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.5 2005/02/27 08:21:15 norby Exp $ */ /* * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> @@ -28,6 +28,7 @@ struct vertex { RB_ENTRY(vertex) entry; + TAILQ_ENTRY(vertex) cand; struct event ev; struct in_addr nexthop; struct vertex *prev; @@ -54,12 +55,22 @@ struct rde_nbr { int self; }; +struct rt_node { + RB_ENTRY(rt_node) entry; + struct in_addr prefix; + u_int8_t prefixlen; + struct in_addr nexthop; + enum path_type p_type; + u_int32_t cost; +}; + /* rde.c */ pid_t rde(struct ospfd_conf *, int [2], int [2], int [2]); int rde_imsg_compose_parent(int, pid_t, void *, u_int16_t); int rde_imsg_compose_ospfe(int, u_int32_t, pid_t, void *, u_int16_t); u_int32_t rde_router_id(void); +void rde_send_kroute(struct rt_node *); void rde_nbr_del(struct rde_nbr *); int rde_nbr_loading(struct area *); struct rde_nbr *rde_nbr_self(struct area *); @@ -74,10 +85,38 @@ int lsa_self(struct rde_nbr *, struct lsa *, struct vertex *); void lsa_add(struct rde_nbr *, struct lsa *); void lsa_del(struct rde_nbr *, struct lsa_hdr *); struct vertex *lsa_find(struct area *, u_int8_t, u_int32_t, u_int32_t); +struct vertex *lsa_find_net(struct area *area, u_int32_t); +int lsa_num_links(struct vertex *); void lsa_snap(struct area *, u_int32_t); void lsa_dump(struct lsa_tree *, pid_t); void lsa_merge(struct rde_nbr *, struct lsa *, struct vertex *); +/* rde_spf.c */ +void spf_calc(struct area *); +void spf_tree_clr(struct area *); + +void cand_list_init(void); +void cand_list_add(struct vertex *); +struct vertex *cand_list_pop(void); +bool cand_list_present(struct vertex *); +void cand_list_clr(void); +bool cand_list_empty(void); + +void spf_timer(int, short, void *); +int start_spf_timer(struct ospfd_conf *); +int stop_spf_timer(struct ospfd_conf *); +int start_spf_holdtimer(struct ospfd_conf *); + +void rt_init(void); +int rt_compare(struct rt_node *, struct rt_node *); +struct rt_node *rt_find(in_addr_t, u_int8_t); +int rt_insert(struct rt_node *); +int rt_remove(struct rt_node *); +void rt_clear(void); + +struct lsa_rtr_link *get_rtr_link(struct vertex *, int); +struct lsa_net_link *get_net_link(struct vertex *, int); + RB_PROTOTYPE(lsa_tree, vertex, entry, lsa_compare) #endif /* _RDE_H_ */ diff --git a/usr.sbin/ospfd/rde_lsdb.c b/usr.sbin/ospfd/rde_lsdb.c index 79af6ed4c61..a7ed88df490 100644 --- a/usr.sbin/ospfd/rde_lsdb.c +++ b/usr.sbin/ospfd/rde_lsdb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_lsdb.c,v 1.9 2005/02/09 22:58:08 claudio Exp $ */ +/* $OpenBSD: rde_lsdb.c,v 1.10 2005/02/27 08:21:15 norby Exp $ */ /* * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> @@ -424,6 +424,38 @@ lsa_find(struct area *area, u_int8_t type, u_int32_t ls_id, u_int32_t adv_rtr) return (v); } +struct vertex * +lsa_find_net(struct area *area, u_int32_t ls_id) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v; + + RB_FOREACH(v, lsa_tree, tree) { + if ((v->lsa->hdr.type == LSA_TYPE_NETWORK) && + (v->lsa->hdr.ls_id == (ls_id))) { + return (v); + } + } + + return (NULL); +} + +int +lsa_num_links(struct vertex *v) +{ + switch (v->type) { + case LSA_TYPE_ROUTER: + return (ntohs(v->lsa->data.rtr.nlinks)); + case LSA_TYPE_NETWORK: + return ((ntohs(v->lsa->hdr.len) - sizeof(struct lsa_hdr) + - sizeof(u_int32_t)) / sizeof(struct lsa_net_link)); + default: + fatalx("lsa_num_links: invalid LSA type"); + } + + return (0); +} + void lsa_snap(struct area *area, u_int32_t peerid) { @@ -520,11 +552,11 @@ lsa_merge(struct rde_nbr *nbr, struct lsa *lsa, struct vertex *v) /* overwrite the lsa all other fields are unaffected */ free(v->lsa); v->lsa = lsa; - + /* set correct timeout for reflooding the LSA */ now = time(NULL); timerclear(&tv); - if (v->changed + MIN_LS_ARRIVAL >= now) + if (v->changed + MIN_LS_ARRIVAL >= now) tv.tv_sec = MIN_LS_ARRIVAL; evtimer_add(&v->ev, &tv); } diff --git a/usr.sbin/ospfd/rde_spf.c b/usr.sbin/ospfd/rde_spf.c new file mode 100644 index 00000000000..25423a5cb5e --- /dev/null +++ b/usr.sbin/ospfd/rde_spf.c @@ -0,0 +1,715 @@ +/* $OpenBSD: rde_spf.c,v 1.1 2005/02/27 08:21:15 norby Exp $ */ + +/* + * Copyright (c) 2005 Esben Norby <norby@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 <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <stdlib.h> + +#include "ospfd.h" +#include "ospf.h" +#include "log.h" +#include "rde.h" + +extern struct ospfd_conf *rdeconf; +TAILQ_HEAD(, vertex) cand_list; +RB_HEAD(rt_tree, rt_node) rt_tree, rt; +RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare) +RB_GENERATE(rt_tree, rt_node, entry, rt_compare) +struct vertex *spf_root = NULL; + +void spf_dump(struct area *); /* XXX */ +void rt_dump(void); /* XXX */ +void cand_list_dump(void); /* XXX */ +void calc_next_hop(struct vertex *, struct vertex *); +void rt_update(struct in_addr, u_int8_t, struct in_addr, u_int32_t, u_int8_t); +bool linked(struct vertex *, struct vertex *); +u_int8_t mask2prefixlen(in_addr_t); + +void +spf_dump(struct area *area) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v; + struct vertex *p; /* parent */ + struct in_addr addr; + + log_debug("spf_dump:"); + RB_FOREACH(v, lsa_tree, tree) { + addr.s_addr = htonl(v->ls_id); + log_debug(" id %s type %d cost %d", inet_ntoa(addr), + v->type, v->cost); + log_debug(" nexthop: %s", inet_ntoa(v->nexthop)); + +#if 1 + log_debug(" --------------------------------------------"); + p = v->prev; + while (p != NULL) { + addr.s_addr = htonl(p->ls_id); + log_debug(" id %15s type %d cost %d", + inet_ntoa(addr), p->type, p->cost); + p = p->prev; + } + log_debug(""); +#endif + } + + return; +} + +void +rt_dump(void) +{ + struct rt_node *r; + int i = 0; + + log_debug("rt_dump:"); + + RB_FOREACH(r, rt_tree, &rt) { + log_debug("net: %s/%d", inet_ntoa(r->prefix), r->prefixlen); + log_debug(" nexthop: %s cost: %d type: %s", + inet_ntoa(r->nexthop), r->cost, path_type_names[r->p_type]); + i++; + + rde_send_kroute(r); + } + + log_debug("count: %d", i); +} + +void +spf_calc(struct area *area) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v, *w; + struct lsa_rtr_link *rtr_link = NULL; + struct lsa_net_link *net_link = NULL; + u_int32_t d; + int i; + struct in_addr addr; + + log_debug("spf_calc: calculation started, area ID %s", + inet_ntoa(area->id)); + + /* clear SPF tree */ + spf_tree_clr(area); + cand_list_clr(); + + /* initialize SPF tree */ + if ((v = spf_root = lsa_find(area, LSA_TYPE_ROUTER, rde_router_id(), + rde_router_id())) == NULL) + fatalx("spf_calc: cannot find self originated router LSA"); + area->transit = false; + spf_root->cost = 0; + w = NULL; + + /* calculate SPF tree */ + do { + /* loop links */ + for (i = 0; i < lsa_num_links(v); i++) { + switch (v->type) { + case LSA_TYPE_ROUTER: + rtr_link = get_rtr_link(v, i); + switch (rtr_link->type) { + case LINK_TYPE_STUB_NET: + /* skip */ + continue; + case LINK_TYPE_POINTTOPOINT: + case LINK_TYPE_VIRTUAL: + /* find router LSA */ + w = lsa_find(area, LSA_TYPE_ROUTER, + rtr_link->id, rtr_link->id); + break; + case LINK_TYPE_TRANSIT_NET: + /* find network LSA */ + w = lsa_find_net(area, rtr_link->id); + break; + default: + fatalx("spf_calc: invalid link type"); + } + break; + case LSA_TYPE_NETWORK: + net_link = get_net_link(v, i); + /* find router LSA */ + w = lsa_find(area, LSA_TYPE_ROUTER, + net_link->att_rtr, net_link->att_rtr); + break; + default: + fatalx("spf_calc: invalid LSA type"); + } + + if (w == NULL) { + log_debug("spf_calc: w = NULL"); + continue; + } + + if (w->lsa->hdr.age == MAX_AGE) { + log_debug("spf_calc: age = MAX_AGE"); + continue; + } + + if (!linked(w, v)) { + log_debug("spf_calc: w has no link to v"); + continue; + } +#if 0 + if ((w->cost != LS_INFINITY) && (v->prev != NULL)) { + log_debug("spf_calc: w already in SPF tree"); + continue; + } +#endif + if (v->type == LSA_TYPE_ROUTER) + d = v->cost + ntohs(rtr_link->metric); + else + d = v->cost; + + if (cand_list_present(w)) { + if (d > w->cost) + continue; + + if (d == w->cost) { + /* calc next hop */ + calc_next_hop(w, v); + } + + if (d < w->cost) { + /* calc next hop */ + calc_next_hop(w, v); + + w->cost = d; + w->prev = v; + } + } else { + if (w->cost == LS_INFINITY) { + w->cost = 0; + w->cost += d; + + cand_list_add(w); + w->prev = v; + + /* calc next hop */ + calc_next_hop(w, v); + } + } + } + + cand_list_dump(); + + /* get next vertex */ + v = cand_list_pop(); + w = NULL; + + } while (v != NULL); + + /* calculate route table */ + RB_FOREACH(v, lsa_tree, tree) { + if (ntohs(v->lsa->hdr.age) == MAX_AGE) + continue; + + switch (v->type) { + case LSA_TYPE_ROUTER: + /* stub networks */ + if ((v->cost == LS_INFINITY) || + (v->nexthop.s_addr == 0)) + continue; + + for (i = 0; i < lsa_num_links(v); i++) { + rtr_link = get_rtr_link(v, i); + if (rtr_link->type != LINK_TYPE_STUB_NET) + continue; + + addr.s_addr = rtr_link->id; + + rt_update(addr, mask2prefixlen(rtr_link->data), + v->nexthop, v->cost + + ntohs(rtr_link->metric), PT_INTRA_AREA); + } + break; + case LSA_TYPE_NETWORK: + if ((v->cost == LS_INFINITY) || + (v->nexthop.s_addr == 0)) + continue; + + addr.s_addr = htonl(v->ls_id) & v->lsa->data.net.mask; + rt_update(addr, mask2prefixlen(v->lsa->data.net.mask), + v->nexthop, v->cost, PT_INTRA_AREA); + break; + case LSA_TYPE_SUM_NETWORK: + if (rdeconf->flags & OSPF_RTR_B) + continue; + + if ((w = lsa_find(area, LSA_TYPE_ROUTER, + htonl(v->adv_rtr), + htonl(v->adv_rtr))) == NULL) + continue; + + v->nexthop = w->nexthop; + v->cost = w->cost + + ntohl(v->lsa->data.sum.metric); + + if (v->nexthop.s_addr == 0) + continue; + + addr.s_addr = htonl(v->ls_id) & v->lsa->data.sum.mask; + rt_update(addr, mask2prefixlen(v->lsa->data.sum.mask), + v->nexthop, v->cost, PT_INTER_AREA); + break; + case LSA_TYPE_SUM_ROUTER: + break; + case LSA_TYPE_EXTERNAL: + break; + default: + fatalx("spf_calc: invalid LSA type"); + } + } + + spf_dump(area); + rt_dump(); + log_debug("spf_calc: calculation ended, area ID %s", + inet_ntoa(area->id)); + + start_spf_timer(rdeconf); + + return ; +} + +void +spf_tree_clr(struct area *area) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v; + + RB_FOREACH(v, lsa_tree, tree) { + v->cost = LS_INFINITY; + v->prev = NULL; + v->nexthop.s_addr = 0; + } +} + +void +calc_next_hop(struct vertex *dst, struct vertex *parent) +{ + struct lsa_rtr_link *rtr_link = NULL; + int i; + + /* case 1 */ + if (parent == spf_root) { + switch (dst->type) { + case LSA_TYPE_ROUTER: + /* XXX */ + log_debug("calc_next_hop: router not handled"); + break; + case LSA_TYPE_NETWORK: + /* XXX */ + log_debug("calc_next_hop: net not handled"); + break; + default: + fatalx("calc_next_hop: invalid dst type"); + } + + return ; + } + + /* case 2 */ + if (parent->type == LSA_TYPE_NETWORK && dst->type == LSA_TYPE_ROUTER && + dst->prev == parent && parent->prev == spf_root) { + for (i = 0; i < lsa_num_links(dst); i++) { + rtr_link = get_rtr_link(dst, i); + if ((rtr_link->type == LINK_TYPE_TRANSIT_NET) && + (rtr_link->data & parent->lsa->data.net.mask) == + (htonl(parent->ls_id) & parent->lsa->data.net.mask)) + dst->nexthop.s_addr = rtr_link->data; + } + + return ; + } + /* case 3 */ + dst->nexthop = parent->nexthop; + + return ; +} + +/* candidate list */ +void +cand_list_init(void) +{ + TAILQ_INIT(&cand_list); +} + +void +cand_list_add(struct vertex *v) +{ + struct vertex *c = NULL; + + /* XXX TODO: network vertex takes precedence over router vertex */ + TAILQ_FOREACH(c, &cand_list, cand) { + if (c->cost > v->cost) { + TAILQ_INSERT_BEFORE(c, v, cand); + return; + } + } + TAILQ_INSERT_TAIL(&cand_list, v, cand); + + return ; +} + +void +cand_list_dump(void) +{ + struct vertex *c = NULL; + struct in_addr addr; + + log_debug("cand_list_dump:"); + TAILQ_FOREACH(c, &cand_list, cand) { + addr.s_addr = htonl(c->ls_id); + log_debug(" id %s type %d cost %d", inet_ntoa(addr), + c->type, c->cost); + } + log_debug(""); +} + +struct vertex * +cand_list_pop(void) +{ + struct vertex *c; + + if ((c = TAILQ_FIRST(&cand_list)) != NULL) { + TAILQ_REMOVE(&cand_list, c, cand); + } + + return (c); +} + +bool +cand_list_present(struct vertex *v) +{ + struct vertex *c; + + TAILQ_FOREACH(c, &cand_list, cand) { + if (c == v) + return (true); + } + + return (false); +} + +void +cand_list_clr(void) +{ + struct vertex *c; + + while ((c = TAILQ_FIRST(&cand_list)) != NULL) { + TAILQ_REMOVE(&cand_list, c, cand); + } +} + +bool +cand_list_empty(void) +{ + return (TAILQ_EMPTY(&cand_list)); +} + +/* timers */ +void +spf_timer(int fd, short event, void *arg) +{ + struct ospfd_conf *conf = arg; + struct area *area; + + log_debug("spf_timer:"); + + switch (conf->spf_state) { + case SPF_IDLE: + fatalx("spf_timer: invalid state IDLE"); + case SPF_HOLDQUEUE: + log_debug("spf_timer: HOLDQUEUE -> DELAY"); + conf->spf_state = SPF_DELAY; + /* FALLTHROUGH */ + case SPF_DELAY: + rt_clear(); /* XXX we should save to old rt! */ + LIST_FOREACH(area, &conf->area_list, entry) { + spf_calc(area); + } + start_spf_holdtimer(rdeconf); + break; + case SPF_HOLD: + log_debug("spf_timer: state HOLD -> IDLE"); + conf->spf_state = SPF_IDLE; + break; + default: + fatalx("spf_timer: unknown state"); + } +} + +int +start_spf_timer(struct ospfd_conf *conf) +{ + struct timeval tv; + + log_debug("start_spf_timer:"); + + switch (conf->spf_state) { + case SPF_IDLE: + log_debug("start_spf_timer: IDLE -> DELAY"); + timerclear(&tv); + tv.tv_sec = conf->spf_delay; + conf->spf_state = SPF_DELAY; + return (evtimer_add(&conf->spf_timer, &tv)); + case SPF_DELAY: + /* ignore */ + break; + case SPF_HOLD: + log_debug("start_spf_timer: HOLD -> HOLDQUEUE"); + conf->spf_state = SPF_HOLDQUEUE; + break; + case SPF_HOLDQUEUE: + break; + default: + fatalx("start_spf_timer: invalid spf_state"); + } + + return (1); +} + +int +stop_spf_timer(struct ospfd_conf *conf) +{ + return (evtimer_del(&conf->spf_timer)); +} + +int +start_spf_holdtimer(struct ospfd_conf *conf) +{ + struct timeval tv; + + switch (conf->spf_state) { + case SPF_DELAY: + timerclear(&tv); + tv.tv_sec = conf->spf_hold_time; + conf->spf_state = SPF_HOLD; + log_debug("spf_start_holdtimer: DELAY -> HOLD"); + return (evtimer_add(&conf->spf_timer, &tv)); + case SPF_IDLE: + case SPF_HOLD: + case SPF_HOLDQUEUE: + fatalx("start_spf_holdtimer: invalid state"); + default: + fatalx("spf_start_holdtimer: unknown state"); + } + + return (1); +} + +/* route table */ +void +rt_init(void) +{ + RB_INIT(&rt); +} + +int +rt_compare(struct rt_node *a, struct rt_node *b) +{ + if (ntohl(a->prefix.s_addr) < ntohl(b->prefix.s_addr)) + return (-1); + if (ntohl(a->prefix.s_addr) > ntohl(b->prefix.s_addr)) + return (1); + if (a->prefixlen < b->prefixlen) + return (-1); + if (a->prefixlen > b->prefixlen) + return (1); + return (0); +} + +struct rt_node * +rt_find(in_addr_t prefix, u_int8_t prefixlen) +{ + struct rt_node s; + + s.prefix.s_addr = prefix; + s.prefixlen = prefixlen; + + return (RB_FIND(rt_tree, &rt, &s)); +} + +int +rt_insert(struct rt_node *r) +{ + if (RB_INSERT(rt_tree, &rt, r) != NULL) { + log_warnx("rt_insert failed for %s/%u", + inet_ntoa(r->prefix), r->prefixlen); + free(r); + return (-1); + } + + return (0); +} + +int +rt_remove(struct rt_node *r) +{ + if (RB_REMOVE(rt_tree, &rt, r) == NULL) { + log_warnx("rt_remove failed for %s/%u", + inet_ntoa(r->prefix), r->prefixlen); + return (-1); + } + + free(r); + return (0); +} + +void +rt_clear(void) +{ + struct rt_node *r; + + while ((r = RB_MIN(rt_tree, &rt)) != NULL) + rt_remove(r); +} + +void +rt_update(struct in_addr prefix, u_int8_t prefixlen, struct in_addr nexthop, + u_int32_t cost, u_int8_t p_type) +{ + struct rt_node *rte; + + if ((rte = rt_find(prefix.s_addr, prefixlen)) == NULL) { + log_debug("rt_update: creating %s/%d", inet_ntoa(prefix), + prefixlen); + if ((rte = calloc(1, sizeof(struct rt_node))) == NULL) + fatalx("rt_update"); + rte->prefix.s_addr = prefix.s_addr; + rte->prefixlen = prefixlen; + rte->nexthop.s_addr = nexthop.s_addr; + rte->cost = cost; + rte->p_type = p_type; + + rt_insert(rte); + } else { + log_debug("rt_update: updating %s/%d", inet_ntoa(prefix), + prefixlen); + + /* XXX better route ? */ + /* consider intra vs. inter */ + if (cost < rte->cost) { + rte->nexthop.s_addr = nexthop.s_addr; + rte->cost = cost; + rte->p_type = p_type; + } + } +} + +/* router LSA links */ +struct lsa_rtr_link * +get_rtr_link(struct vertex *v, int idx) +{ + struct lsa_rtr_link *rtr_link = NULL; + char *buf = (char *)v->lsa; + u_int16_t i, off, nlinks; + + if (v->type != LSA_TYPE_ROUTER) + fatalx("get_rtr_link: invalid LSA type"); + + off = sizeof(v->lsa->hdr) + sizeof(struct lsa_rtr); + + nlinks = lsa_num_links(v); + for (i = 0; i < nlinks; i++) { + rtr_link = (struct lsa_rtr_link *)(buf + off); + if (i == idx) + return (rtr_link); + + off += sizeof(struct lsa_rtr_link) + + rtr_link->num_tos * sizeof(u_int32_t); + } + + return (NULL); +} + +/* network LSA links */ +struct lsa_net_link * +get_net_link(struct vertex *v, int idx) +{ + struct lsa_net_link *net_link = NULL; + char *buf = (char *)v->lsa; + u_int16_t i, off, nlinks; + + if (v->type != LSA_TYPE_NETWORK) + fatalx("get_net_link: invalid LSA type"); + + off = sizeof(v->lsa->hdr) + sizeof(u_int32_t); + + nlinks = lsa_num_links(v); + for (i = 0; i < nlinks; i++) { + net_link = (struct lsa_net_link *)(buf + off); + if (i == idx) + return (net_link); + + off += sizeof(struct lsa_net_link); + } + + return (NULL); +} + +/* misc */ +bool +linked(struct vertex *w, struct vertex *v) +{ + struct lsa_rtr_link *rtr_link = NULL; + struct lsa_net_link *net_link = NULL; + int i; + + switch (w->type) { + case LSA_TYPE_ROUTER: + for (i = 0; i < lsa_num_links(w); i++) { + rtr_link = get_rtr_link(w, i); + switch (v->type) { + case LSA_TYPE_ROUTER: + if (rtr_link->type == LINK_TYPE_POINTTOPOINT && + rtr_link->id == htonl(v->ls_id)) + return (true); + break; + case LSA_TYPE_NETWORK: + if (rtr_link->id == htonl(v->ls_id)) + return (true); + break; + default: + fatalx("spf_calc: invalid type"); + } + } + return (false); + case LSA_TYPE_NETWORK: + for (i = 0; i < lsa_num_links(w); i++) { + net_link = get_net_link(w, i); + switch (v->type) { + case LSA_TYPE_ROUTER: + if (net_link->att_rtr == htonl(v->ls_id)) + return (true); + break; + default: + fatalx("spf_calc: invalid type"); + } + } + return (false); + default: + fatalx("spf_calc: invalid LSA type"); + } + + return (false); +} |