0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-10-18 09:58:43 +00:00

Some preliminary IS-IS commit.

This commit is contained in:
Ondrej Zajicek 2012-08-29 12:35:28 +02:00
parent 8ecbaf9c70
commit fed1bceb2c
13 changed files with 1527 additions and 2 deletions

View File

@ -72,6 +72,14 @@ void config_add_obstacle(struct config *);
void config_del_obstacle(struct config *);
void order_shutdown(void);
static inline void
cf_range(const char *opt, int val, int min, int max)
{
if ((val < min) || (val > max))
cf_error("%s must be in range %d-%d", opt, min, max);
}
#define CONF_DONE 0
#define CONF_PROGRESS 1
#define CONF_QUEUED 2

View File

@ -26,6 +26,13 @@ CF_HDR
CF_DEFINES
static void
check_u8(unsigned val)
{
if (val > 0xFF)
cf_error("Value %d out of range (0-255)", val);
}
static void
check_u16(unsigned val)
{

View File

@ -51,7 +51,7 @@ if test "$enable_ipv6" = yes ; then
else
ip=ipv4
SUFFIX=""
all_protocols=bgp,ospf,pipe,rip,static
all_protocols=bgp,ospf,isis,pipe,rip,static
fi
if test "$given_suffix" = yes ; then

View File

@ -701,6 +701,9 @@ protos_build(void)
#ifdef CONFIG_OSPF
proto_build(&proto_ospf);
#endif
#ifdef CONFIG_ISIS
proto_build(&proto_isis);
#endif
#ifdef CONFIG_PIPE
proto_build(&proto_pipe);
#endif

View File

@ -75,7 +75,7 @@ void protos_dump_all(void);
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static,
proto_ospf, proto_pipe, proto_bgp;
proto_ospf, proto_isis, proto_pipe, proto_bgp;
/*
* Routing Protocol Instance

5
proto/isis/Makefile Normal file
View File

@ -0,0 +1,5 @@
source=isis.c ifaces.c lsdb.c packets.c
root-rel=../../
dir-name=proto/isis
include ../../Rules

187
proto/isis/config.Y Normal file
View File

@ -0,0 +1,187 @@
/*
* BIRD -- IS-IS Configuration
*
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
CF_HDR
#include "proto/isis/isis.h"
CF_DEFINES
#define ISIS_CFG ((struct isis_config *) this_proto)
#define ISIS_IFACE ((struct isis_iface_config *) this_ipatt)
static byte area_id_buffer[13];
static byte *area_id_bpos;
CF_DECLS
CF_KEYWORDS(AREA, BCAST, BROADCAST, BUFFER, CSNP, HELLO, HOLD, ID, INTERFACE, ISIS, LEVEL, LIFETIME,
LSP, METRIC, MULT, PASSIVE, POINTOPOINT, PRIORITY, PSNP, PTP, RETRANSMIT, RX, SIZE, SYSTEM,
TX, TYPE)
%type<i> isis_level
CF_GRAMMAR
CF_ADDTO(proto, isis_proto)
isis_proto_start: proto_start ISIS
{
this_proto = proto_config_new(&proto_isis, sizeof(struct isis_config), $1);
init_list(&ISIS_CFG->patt_list);
ISIS_CFG->lsp_lifetime = ISIS_DEFAULT_LSP_LIFETIME;
ISIS_CFG->rx_buffer_size = ISIS_DEFAULT_RX_BUFFER_SIZE;
ISIS_CFG->tx_buffer_size = ISIS_DEFAULT_TX_BUFFER_SIZE;
};
isis_proto_item:
proto_item
| INTERFACE isis_iface
| AREA ID isis_area_id
| SYSTEM ID isis_system_id
| LSP LIFETIME expr { ISIS_CFG->lsp_lifetime = $3; cf_range("LSP lifetime", $3, 300, 65535); }
| RX BUFFER SIZE expr { ISIS_CFG->rx_buffer_size = $4; cf_range("RX buffer size", $4, 256, 65535); }
| TX BUFFER SIZE expr { ISIS_CFG->tx_buffer_size = $4; cf_range("TX buffer size", $4, 256, 65535); }
;
isis_proto_finish:
{
struct isis_config *cf = ISIS_CFG;
if (! cf->areas[0])
cf_error("Missing area ID");
if (cf->rx_buffer_size < cf->tx_buffer_size)
cf_error("RX buffer size must be greater than TX buffer size");
}
isis_proto_opts:
/* empty */
| isis_proto_opts isis_proto_item ';'
;
isis_proto:
isis_proto_start proto_name '{' isis_proto_opts '}';
isis_iface_start:
{
this_ipatt = cfg_allocz(sizeof(struct isis_iface_config));
add_tail(&ISIS_CFG->patt_list, NODE this_ipatt);
ISIS_IFACE->levels[ISIS_L1] = ISIS_DEFAULT_LEVEL_1;
ISIS_IFACE->levels[ISIS_L2] = ISIS_DEFAULT_LEVEL_2;
ISIS_IFACE->metric = ISIS_DEFAULT_METRIC;
ISIS_IFACE->priority = ISIS_DEFAULT_PRIORITY;
ISIS_IFACE->hello_int = ISIS_DEFAULT_HELLO_INT;
ISIS_IFACE->hold_int = ISIS_DEFAULT_HOLD_INT;
ISIS_IFACE->hold_mult = ISIS_DEFAULT_HOLD_MULT;
ISIS_IFACE->rxmt_int = ISIS_DEFAULT_RXMT_INT;
ISIS_IFACE->csnp_int = ISIS_DEFAULT_CSNP_INT;
ISIS_IFACE->psnp_int = ISIS_DEFAULT_PSNP_INT;
};
isis_level: expr { $$ = $1 - 1; if (($1 < 1) || ($1 > 2)) cf_error("Level must be 1 or 2"); }
isis_iface_item:
LEVEL isis_level bool { ISIS_IFACE->levels[$2] = $3; }
| LEVEL isis_level PASSIVE { ISIS_IFACE->levels[$2] = ISIS_LEVEL_PASSIVE; }
| PASSIVE bool { ISIS_IFACE->passive = $2; }
| TYPE BROADCAST { ISIS_IFACE->type = ISIS_IT_BCAST; }
| TYPE BCAST { ISIS_IFACE->type = ISIS_IT_BCAST; }
| TYPE POINTOPOINT { ISIS_IFACE->type = ISIS_IT_PTP; }
| TYPE PTP { ISIS_IFACE->type = ISIS_IT_PTP; }
| METRIC expr { ISIS_IFACE->metric = $2; cf_range("Metric", $2, 1, 63); }
| PRIORITY expr { ISIS_IFACE->priority = $2; cf_range("Priority", $2, 1, 127); }
| HELLO expr { ISIS_IFACE->hello_int = $2; cf_range("Hello interval", $2, 1, 65535); }
| HOLD expr { ISIS_IFACE->hold_int = $2; cf_range("Hold interval", $2, 3, 65535); }
| HOLD MULT expr { ISIS_IFACE->hold_mult = $3; cf_range("Hold multiplier", $3, 3, 255); }
| RETRANSMIT expr { ISIS_IFACE->rxmt_int = $2; cf_range("Retransmit interval", $2, 3, 65535); }
| CSNP expr { ISIS_IFACE->csnp_int = $2; cf_range("CSNP interval", $2, 1, 65535); }
| PSNP expr { ISIS_IFACE->psnp_int = $2; cf_range("PSNP interval", $2, 1, 65535); }
;
isis_iface_finish:
{
struct isis_iface_config *ic = ISIS_IFACE;
if (! ic->hold_int)
{
u32 hold_int = ic->hold_mult * (u32) ic->hello_int;
if (hold_int > 65535)
cf_error("Hello interval times Hold multiplier greater than 65535");
ic->hold_int = hold_int;
}
};
isis_iface_opts:
/* empty */
| isis_iface_opts isis_iface_item ';'
;
isis_iface_opt_list:
/* empty */
| '{' isis_iface_opts '}'
;
isis_iface:
isis_iface_start iface_patt_list isis_iface_opt_list isis_iface_finish;
isis_area_id_read:
NUM
{
check_u8($1);
if ($1 == 0)
cf_error("Area ID must not start with 0");
area_id_bpos = area_id_buffer;
*area_id_bpos++ = $1;
}
| isis_area_id_read '-' NUM
{
check_u16($3);
if ((area_id_bpos + 2 - area_id_buffer) > sizeof(area_id_buffer))
cf_error("Area ID too long");
put_u16(area_id_bpos, $3);
area_id_bpos += 2;
}
;
isis_area_id: isis_area_id_read
{
struct isis_area_id *area_id;
int i, blen;
for (i = 0; i < ISIS_AREAS; i++)
if (! ISIS_CFG->areas[i])
goto found;
cf_error("Too many areas");
found:
blen = area_id_bpos - area_id_buffer;
area_id = cfg_allocz(1 + blen);
area_id->length = blen;
memcpy(area_id->body, area_id_buffer, blen);
ISIS_CFG->areas[i] = area_id;
}
isis_system_id: NUM '-' NUM '-' NUM
{
check_u16($1); check_u16($3); check_u16($5);
ISIS_CFG->system_id = (((u64) $1) << 48) | (((u64) $3) << 32) | (((u64) $5) << 16);
}
CF_CODE
CF_END

354
proto/isis/ifaces.c Normal file
View File

@ -0,0 +1,354 @@
/*
* BIRD -- IS-IS Interfaces and Neighbors
*
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include "isis.h"
static char* ev_name[] = { NULL, "Init", "Change", "RS" };
#ifdef XXX
static void
isis_timer(timer *tm)
{
struct isis_iface *ifa = tm->data;
struct isis_proto *p = ifa->ra;
ISIS_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
isis_send_ra(ifa, 0);
/* Update timer */
ifa->last = now;
unsigned after = ifa->cf->min_ra_int;
after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
if (ifa->initial)
ifa->initial--;
if (ifa->initial)
after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
tm_start(ifa->timer, after);
}
void
isis_iface_notify(struct isis_iface *ifa, int event)
{
struct isis_proto *p = ifa->p;
if (!ifa->sk)
return;
ISIS_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
switch (event)
{
case RA_EV_CHANGE:
ifa->plen = 0;
case RA_EV_INIT:
ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
break;
case RA_EV_RS:
break;
}
/* Update timer */
unsigned delta = now - ifa->last;
unsigned after = 0;
if (delta < ifa->cf->min_delay)
after = ifa->cf->min_delay - delta;
tm_start(ifa->timer, after);
}
#endif
/*
isis_dr_election()
{
at least one up, all relevant neighbors and myself
local system becomes or resign -> event lANLevel1/2DesignatedIntermediateSystemChange
becomes ->
set lan-id
originate new and purge old pseudonode LSP
zmena dr -> zmenit lan-id
zmena lan-id -> zmena me LSP
}
*/
static struct isis_iface *
isis_iface_find(struct isis_proto *p, struct iface *what)
{
struct isis_iface *ifa;
WALK_LIST(ifa, p->iface_list)
if (ifa->iface == what)
return ifa;
return NULL;
}
static void
iface_olock_hook(struct object_lock *lock)
{
struct isis_iface *ifa = lock->data;
struct isis_proto *p = ifa->p;
if (! isis_sk_open(ifa))
{
log(L_ERR "%s: Socket open failed on interface %s", p->p.name, ifa->iface->name);
return;
}
// XXX isis_iface_notify(ifa, RA_EV_INIT);
}
static void
l1_hello_timer_hook(timer *timer)
{
struct isis_iface *ifa = (struct isis_iface *) timer->data;
isis_send_lan_hello(ifa, ISIS_L1);
}
static void
l2_hello_timer_hook(timer *timer)
{
struct isis_iface *ifa = (struct isis_iface *) timer->data;
isis_send_lan_hello(ifa, ISIS_L2);
}
static void
ptp_hello_timer_hook(timer *timer)
{
struct isis_iface *ifa = (struct isis_iface *) timer->data;
isis_send_ptp_hello(ifa);
}
static void
csnp_timer_hook(timer *timer)
{
struct isis_iface *ifa = (struct isis_iface *) timer->data;
struct isis_proto *p = ifa->p;
struct isis_lsp *lsp;
int n;
/* FIXME: CSNP rate limiting */
if (XXX)
{
n = 0;
lsp = isis_lsdb_first(p->lsdb[ISIS_L1], lsp, ifa);
while (lsp)
isis_send_csnp(ifa, ISIS_L1, &lsp, n++ == 0);
}
if (XXX)
{
n = 0;
lsp = isis_lsdb_first(p->lsdb[ISIS_L2], lsp, ifa);
while (lsp)
isis_send_csnp(ifa, ISIS_L2, &lsp, n++ == 0);
}
}
static void
psnp_timer_hook(timer *timer)
{
struct isis_iface *ifa = (struct isis_iface *) timer->data;
struct isis_proto *p = ifa->p;
struct isis_lsp *lsp;
if (XXX)
{
lsp = isis_lsdb_first_ssn(p->lsdb[ISIS_L1], lsp, ifa);
while (lsp)
isis_send_psnp(ifa, ISIS_L1, &lsp);
}
if (XXX)
{
lsp = isis_lsdb_first_ssn(p->lsdb[ISIS_L2], lsp, ifa);
while (lsp)
isis_send_psnp(ifa, ISIS_L2, &lsp);
}
}
static void
isis_iface_new(struct isis_proto *p, struct iface *iface, struct isis_iface_config *cf)
{
ISIS_TRACE(D_EVENTS, "Adding interface %s", iface->name);
pool *pool = rp_new(p->p.pool, "ISIS Interface");
struct isis_iface *ifa = mb_allocz(pool, sizeof(struct isis_iface));
add_tail(&p->iface_list, NODE ifa);
ifa->p = p;
ifa->cf = cf;
ifa->iface = iface;
ifa->pool = pool;
init_list(&ifa->neigh_list);
ifa->type = ifa->cf->type;
ifa->levels = ifa->cf->levels;
ifa->priority = ifa->cf->priority;
ifa->hello_int = ifa->cf->hello_int;
ifa->hold_int = ifa->cf->hold_int;
if (ifa->type == ISIS_IT_PASSIVE)
return;
ifa->hello_timer = tm_new_set(pool, hello_timer_hook, ifa, xxx, xxx);
struct object_lock *lock = olock_new(pool);
lock->addr = IPA_NONE;
lock->type = OBJLOCK_IP;
lock->port = ISIS_PROTO;
lock->iface = iface;
lock->data = ifa;
lock->hook = iface_olock_hook;
ifa->lock = lock;
olock_acquire(lock);
}
/*
static inline void
isis_iface_shutdown(struct isis_iface *ifa)
{
if (ifa->sk)
isis_send_ra(ifa, 1);
}
*/
static void
isis_iface_remove(struct isis_iface *ifa)
{
struct isis_proto *p = ifa->p;
ISIS_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
// XXX isis_iface_sm(ifa, ISM_DOWN);
rem_node(NODE ifa);
rfree(ifa->pool);
}
void
isis_if_notify(struct proto *pp, unsigned flags, struct iface *iface)
{
struct isis_proto *p = (struct isis_proto *) pp;
struct isis_config *cf = (struct isis_config *) (pp->cf);
if (iface->flags & IF_IGNORE)
return;
if (flags & IF_CHANGE_UP)
{
struct isis_iface_config *ic = (struct isis_iface_config *)
iface_patt_find(&cf->patt_list, iface, NULL);
if (ic)
isis_iface_new(p, iface, ic);
return;
}
struct isis_iface *ifa = isis_iface_find(p, iface);
if (!ifa)
return;
if (flags & IF_CHANGE_DOWN)
{
isis_iface_remove(ifa);
return;
}
if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
isis_iface_notify(ifa, RA_EV_INIT);
}
/*
void
isis_ifa_notify(struct proto *pp, unsigned flags, struct ifa *a)
{
struct isis_proto *p = (struct isis_proto *) pp;
if (a->flags & IA_SECONDARY)
return;
if (a->scope <= SCOPE_LINK)
return;
struct isis_iface *ifa = isis_iface_find(ra, a->iface);
if (ifa)
isis_iface_notify(ifa, RA_EV_CHANGE);
}
*/
static void
hold_timer_hook(timer *timer)
{
struct isis_neighbor *n = (struct isis_neighbor *) timer->data;
struct isis_iface *ifa = n->ifa;
struct isis_proto *p = ifa->p;
// xxx ISIS_TRACE(D_EVENTS, "Hold timer expired for neighbor %I", n->ip);
isis_neighbor_remove(n);
}
struct isis_neighbor *
isis_neighbor_add(struct isis_iface *ifa)
{
struct isis_proto *p = ifa->p;
struct isis_neighbor *n = mb_allocz(ifa->pool, sizeof(struct isis_neighbor));
add_tail(&ifa->neigh_list, NODE n);
n->ifa = ifa;
n->hold_timer = tm_new_set(ifa->pool, hold_timer_hook, n, xxx, xxx);
return (n);
}
void
isis_neighbor_remove(struct isis_neighbor *n)
{
struct isis_iface *ifa = n->ifa;
struct isis_proto *p = ifa->p;
// xxx ISIS_TRACE(D_EVENTS, "Removing neigbor");
rem_node(NODE n);
rfree(n->hold_timer);
}
/*
new:
t neighbourSystemType - podle typu paketu
holdingTimer, priorityOfNeighbour, neighbour-SystemID and areaAddressesOfNeighbour - podle obsahu
mac_addr
state -> init
checknout my sysID in list -> up
*/

124
proto/isis/isis.c Normal file
View File

@ -0,0 +1,124 @@
/*
* BIRD -- IS-IS
*
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include "isis.h"
/**
* DOC: Intermediate System to Intermediate System (IS-IS)
*
* Intermediate System to Intermediate System
* intra-domain routeing information exchange protocol
*
* XXXX
*
* Supported standards:
* - ISO 10589 - main IS-IS standard
* - RFC xxxx -
*/
static struct proto *
isis_init(struct proto_config *c)
{
struct proto *pp = proto_new(c, sizeof(struct isis_proto));
pp->if_notify = isis_if_notify;
pp->ifa_notify = isis_ifa_notify;
return pp;
}
static int
isis_start(struct proto *pp)
{
struct isis_proto *p = (struct isis_proto *) pp;
// struct isis_config *cf = (struct isis_config *) (pp->cf);
init_list(&(p->iface_list));
return PS_UP;
}
static int
isis_shutdown(struct proto *pp)
{
struct isis_proto *p = (struct isis_proto *) pp;
struct isis_iface *ifa;
WALK_LIST(ifa, p->iface_list)
isis_iface_shutdown(ifa);
return PS_DOWN;
}
#ifdef XXX
static int
isis_reconfigure(struct proto *pp, struct proto_config *c)
{
struct isis_proto *p = (struct isis_proto *) pp;
// struct isis_config *old = (struct isis_config *) (p->cf);
struct isis_config *new = (struct isis_config *) c;
/*
* The question is why there is a reconfigure function for RAdv if
* it has almost none internal state so restarting the protocol
* would probably suffice. One small reason is that restarting the
* protocol would lead to sending a RA with Router Lifetime 0
* causing nodes to temporary remove their default routes.
*/
struct iface *iface;
WALK_LIST(iface, iface_list)
{
struct isis_iface *ifa = isis_iface_find(ra, iface);
struct isis_iface_config *ic = (struct isis_iface_config *)
iface_patt_find(&new->patt_list, iface, NULL);
if (ifa && ic)
{
ifa->cf = ic;
/* We cheat here - always notify the change even if there isn't
any. That would leads just to a few unnecessary RAs. */
isis_iface_notify(ifa, RA_EV_CHANGE);
}
if (ifa && !ic)
{
isis_iface_shutdown(ifa);
isis_iface_remove(ifa);
}
if (!ifa && ic)
isis_iface_new(ra, iface, ic);
}
return 1;
}
static void
isis_copy_config(struct proto_config *dest, struct proto_config *src)
{
struct isis_config *d = (struct isis_config *) dest;
struct isis_config *s = (struct isis_config *) src;
/* We clean up patt_list, ifaces are non-sharable */
init_list(&d->patt_list);
}
#endif
struct protocol isis_proto = {
.name = "IS-IS",
.template = "isis%d",
.init = isis_init,
.start = isis_start,
.shutdown = isis_shutdown,
// .reconfigure = isis_reconfigure,
// .copy_config = isis_copy_config
};

206
proto/isis/isis.h Normal file
View File

@ -0,0 +1,206 @@
/*
* BIRD -- Router Advertisement
*
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_ISIS_H_
#define _BIRD_ISIS_H_
#include "nest/bird.h"
#include "lib/ip.h"
#include "lib/lists.h"
#include "lib/socket.h"
#include "lib/timer.h"
#include "lib/resource.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/cli.h"
#include "nest/locks.h"
#include "conf/conf.h"
#include "lib/string.h"
#define ISIS_PROTO 90 // XXX
#define ISIS_LEVELS 2
#define ISIS_L1 0
#define ISIS_L2 1
#define ISIS_AREAS 3
#define mac_addr ip_addr // XXX
#define ETH_ALL_ISS ipa_from_u32(0xe0000005) /* 224.0.0.5 */
#define ETH_ALL_L1_ISS ipa_from_u32(0xe0000005) /* 224.0.0.5 */
#define ETH_ALL_L2_ISS ipa_from_u32(0xe0000005) /* 224.0.0.5 */
struct isis_area_id
{
byte length;
byte body[];
};
struct isis_config
{
struct proto_config c;
list patt_list; /* List of iface configs (struct isis_iface_config) */
struct isis_area_id *areas[ISIS_AREAS];
u64 system_id;
u16 lsp_lifetime;
u16 rx_buffer_size;
u16 tx_buffer_size;
};
#define ISIS_DEFAULT_LSP_LIFETIME 1200
#define ISIS_DEFAULT_RX_BUFFER_SIZE 1492
#define ISIS_DEFAULT_TX_BUFFER_SIZE 1492
struct isis_iface_config
{
struct iface_patt i;
u8 levels[ISIS_LEVELS];
u8 passive;
u8 type;
u8 metric;
u8 priority;
u16 hello_int;
u16 hold_int;
u16 hold_mult;
u16 rxmt_int;
u16 csnp_int;
u16 psnp_int;
};
#define ISIS_LEVEL_PASSIVE 2
#define ISIS_DEFAULT_LEVEL_1 1
#define ISIS_DEFAULT_LEVEL_2 0
#define ISIS_DEFAULT_METRIC 1
#define ISIS_DEFAULT_PRIORITY 64
#define ISIS_DEFAULT_HELLO_INT 10
#define ISIS_DEFAULT_HOLD_INT 0
#define ISIS_DEFAULT_HOLD_MULT 3
#define ISIS_DEFAULT_RXMT_INT 5
#define ISIS_DEFAULT_CSNP_INT 10
#define ISIS_DEFAULT_PSNP_INT 2
struct isis_proto
{
struct proto p;
list iface_list; /* List of active ifaces */
u64 system_id;
u16 lsp_max_age;
u16 lsp_refresh;
u16 buffer_size;
};
struct isis_iface
{
node n;
struct isis_proto *p;
struct isis_iface_config *cf; /* Related config, must be updated in reconfigure */
struct iface *iface;
pool *pool;
struct object_lock *lock;
sock *sk;
list neigh_list; /* List of neigbours */
u8 type;
u8 levels;
u8 priority;
u16 hello_int;
u16 hold_int;
};
#define ISIS_IT_UNDEF 0
#define ISIS_IT_BCAST 1
#define ISIS_IT_PTP 2
struct isis_neighbor
{
node n;
struct isis_iface *ifa;
mac_addr addr;
u64 id;
timer *hold_timer;
u8 levels;
u8 priority;
};
struct isis_lsdb
{
pool *pool;
slab *slab;
list list;
};
struct isis_lsp_hdr
{
u64 id;
u32 seqnum;
u16 lifetime;
u16 checksum;
}
struct isis_lsp
{
node n;
struct isis_lsp_hdr hdr;
void *body;
u16 blen;
};
#define RA_EV_INIT 1 /* Switch to initial mode */
#define RA_EV_CHANGE 2 /* Change of options or prefixes */
#define RA_EV_RS 3 /* Received RS */
#ifdef LOCAL_DEBUG
#define ISIS_FORCE_DEBUG 1
#else
#define ISIS_FORCE_DEBUG 0
#endif
#define ISIS_TRACE(flags, msg, args...) do { if ((p->p.debug & flags) || ISIS_FORCE_DEBUG) \
log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0)
/* isis.c */
void isis_iface_notify(struct isis_iface *ifa, int event);
/* ifaces.c */
void isis_if_notify(struct proto *pp, unsigned flags, struct iface *iface);
void isis_ifa_notify(struct proto *pp, unsigned flags, struct ifa *a);
/* packets.c */
void isis_send_lan_hello(struct isis_iface *ifa, int level);
void isis_send_ptp_hello(struct isis_iface *ifa);
void isis_send_lsp(struct isis_iface *ifa, struct lsp_entry *en);
void isis_send_csnp(struct isis_iface *ifa, int level);
void isis_send_psnp(struct isis_iface *ifa, int level);
int isis_sk_open(struct isis_iface *ifa);
#endif /* _BIRD_ISIS_H_ */

89
proto/isis/lsdb.c Normal file
View File

@ -0,0 +1,89 @@
/*
* BIRD -- IS-IS LSP database
*
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
static inline int
isis_lsp_comp(struct isis_lsp_hdr *l1, struct isis_lsp_hdr *l2)
{
if (l1->seqnum != l2->seqnum)
return (l1->seqnum > l2->seqnum) ? LSP_NEWER : LSP_OLDER;
// XXX review
if (l1->checksum != l2->checksum)
return (l1->checksum > l2->checksum) ? LSP_NEWER : LSP_OLDER;
return LSP_SAME;
}
struct isis_lsp *
isis_get_lsp(struct isis_lsdb *db, xxx)
{
}
void
isis_lsp_received(struct isis_lsdb *db, struct isis_iface *ifa,
struct isis_lsp_hdr *hdr, byte *body, int blen)
{
struct isis_lsp *lsp = isis_get_lsp(db, hdr);
int cmp = isis_lsp_comp(hdr, &lsp->hdr);
switch (cmp)
{
LSP_NEWER:
xxx();
LSP_SAME:
isis_lsp_clear_srm(lsp, ifa);
isis_lsp_set_ack(lsp, ifa);
break;
LSP_OLDER:
isis_lsp_set_srm(lsp, ifa);
isis_lsp_clear_ssn(lsp, ifa);
break;
}
}
void
isis_snp_received(struct isis_lsdb *db, struct isis_iface *ifa, struct isis_lsp_hdr *hdr)
{
struct isis_lsp *lsp = isis_get_lsp(db, hdr);
int cmp = isis_lsp_comp(hdr, &lsp->hdr);
switch (cmp)
{
LSP_NEWER:
isis_lsp_set_ssn(lsp, ifa);
LSP_SAME:
isis_lsp_clear_ack(lsp, ifa);
break;
LSP_OLDER:
isis_lsp_set_srm(lsp, ifa);
isis_lsp_clear_ssn(lsp, ifa);
break;
}
isis_lsp_clear_csnp(lsp);
}
struct isis_lsdb *
isis_lsdb_new(struct isis_proto *p)
{
pool *pool = rp_new(p->p.pool, "ISIS LSP database");
struct isis_lsdb *db = mb_allocz(pool, sizeof(struct isis_lsdb));
db->pool = pool;
db->slab = sl_new(pool, sizeof(struct lsp_entry));
init_list(&db->list);
}
void
isis_lsdb_free(struct isis_lsdb *db)
{
rfree(db->pool);
}

541
proto/isis/packets.c Normal file
View File

@ -0,0 +1,541 @@
/*
* BIRD -- IS-IS Packet Processing
*
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include "isis.h"
/*
* IS-IS common packet header
*
* 1B Protocol ID, constant
* 1B Fixed header length depends on packet type
* 1B Version 1 fixed 1
* 1B System ID length fixed 0 or 6
* 1B Packet type 0xe0 bits reserved
* 1B Version 2 fixed 1
* 1B Reserved ignored
* 1B Max area addrs fixed 0 or 3
*
*
* IS-IS Hello header
*
* 8B Common header
* 1B Circuit type levels (1, 2 or 1+2), 0xfc bits reserved
* 6B System ID
* 2B Hold time
* 2B Packet length hdr + data
* LAN:
* 1B Priority 0x80 bit reserved
* 7B LAN ID for DR selection
* PTP:
* 1B Circuit ID we could ignore this (?)
*
*
*
* IS-IS LSP header
*
* 8B Common header
* 2B Packet length hdr + data
* 2B LSP lifetime
* 8B LSP ID
* 4B LSP sequence number
* 2B LSP checksum
* 1B LSP flags
*
*
* IS-IS CSNP header
*
* 8B Common header
* 2B Packet length hdr + data
* 7B System ID
* 8B Start LSP ID
* 8B End LSP ID
*
*
* IS-IS PSNP header
*
* 8B Common header
* 2B Packet length hdr + data
* 7B System ID
*/
#define ISIS_PROTO_ID 0x83
#define ISIS_L1_HELLO 15
#define ISIS_L2_HELLO 16
#define ISIS_PTP_HELLO 17
#define ISIS_L1_LSP 18
#define ISIS_L2_LSP 20
#define ISIS_L1_CSNP 24
#define ISIS_L2_CSNP 25
#define ISIS_L1_PSNP 26
#define ISIS_L2_PSNP 27
#define ISIS_COMMON_HLEN 8
#define ISIS_LHELLO_HLEN 27
#define ISIS_PHELLO_HLEN 20
#define ISIS_LSP_HLEN 27
#define ISIS_CSNP_HLEN 33
#define ISIS_PSNP_HLEN 17
#define ISIS_TYPE_MASK 0x1f
IntradomainRouteingPD
static inline byte * isis_tx_buffer(struct isis_iface *ifa) { return ifa->sk->tbuf; }
static void
isis_fill_hdr(byte *pkt, u8 pdu_type, u8 hdr_len)
{
pkt[0] = ISIS_PROTO_ID;
pkt[1] = hdr_len;
pkt[2] = 1; // Version 1
pkt[3] = 6; // System ID length
pkt[4] = pdu_type;
pkt[5] = 1; // Version 2
pkt[6] = 0; // Reserved
pkt[7] = 3; // Max area addresses
}
static inline byte *
put_lsp_hdr(byte *buf, struct isis_lsp_hdr *hdr)
{
put_u16(buf+ 0, hdr->lifetime);
put_u64(buf+ 2, hdr->id);
put_u32(buf+10, hdr->seqnum);
put_u16(buf+14, hdr->checksum);
return buf+16;
}
static byte *
isis_put_tlv_areas(struct isis_proto *p, byte *buf)
{
byte *bp = buf + 2;
int i;
for (i = 0; i < ISIS_AREAS; i++)
if (cf->areas[i])
{
int alen = 1 + cf->areas[i]->length;
memcpy(bp, cf->areas[i], alen);
bp += alen;
}
buf[0] = ISIS_TLV_AREAS;
buf[1] = bp - (buf + 2);
return bp;
}
static byte *
isis_put_tlv_protocols(struct isis_proto *p, byte *buf)
{
buf[0] = ISIS_TLV_PROTOCOLS;
buf[1] = 1;
buf[2] = ISIS_NLPID_IPv4;
return buf + 3;
}
static byte *
isis_put_tlv_ip4_iface_addrs(struct isis_iface *ifa, byte *buf)
{
struct ifa *a;
byte *bp = buf + 2;
int i;
WALK_LIST(a, ifa->iface->addrs)
{
bp = ipa_put_addr(bp, a);
}
buf[0] = ISIS_TLV_IP4_IFACE_ADDRS;
buf[1] = bp - (buf + 2);
return bp;
}
void
isis_send_lan_hello(struct isis_iface *ifa, int level)
{
struct isis_proto *p = ifa->p;
byte *pkt = isis_tx_buffer(ifa);
isis_fill_hdr(pkt, !level ? ISIS_L1_HELLO : ISIS_L2_HELLO, ISIS_LHELLO_HLEN);
put_u8 (pkt+ 8, ifa->levels);
put_id6(pkt+ 9, p->system_id);
put_u16(pkt+15, ifa->hold_int);
// put_u16(pkt+17, ISIS_ + en->blen);
put_u8 (pkt+19, ifa->priority);
put_id7(pkt+20, 0); // xxx DR
}
void
isis_send_ptp_hello(struct isis_iface *ifa)
{
struct isis_proto *p = ifa->p;
byte *pkt = isis_tx_buffer(ifa);
byte *bp = pkt + ISIS_PHELLO_HLEN;
byte *be = isis_tx_buffer_end(ifa);
isis_fill_hdr(pkt, ISIS_PTP_HELLO, ISIS_PHELLO_HLEN);
put_u8 (pkt+ 8, ifa->levels);
put_id6(pkt+ 9, p->system_id);
put_u16(pkt+15, ifa->hold_int);
// put_u16(pkt+17, 0); /* Length postponed */
put_u8 (pkt+19, 0); /* Fake circuit-id */
bp = isis_put_tlv_areas(p, bp, be);
bp = isis_put_tlv_protocols(p, bp, be);
bp = isis_put_tlv_ip4_iface_addrs(ifa, bp, be);
put_u16(pkt+17, bp - pkt); /* Packet length */
}
void
isis_send_lsp(struct isis_iface *ifa, struct isis_lsp *lsp)
{
struct isis_proto *p = ifa->p;
byte *pkt = isis_tx_buffer(ifa);
isis_fill_hdr(pkt, xxx ? ISIS_L1_LSP : ISIS_L2_LSP, ISIS_LSP_HLEN);
put_u16(pkt+ 8, ISIS_LSP_HLEN - 1 + lsp->blen);
put_lsp_hdr(pkt+10, &lsp->hdr);
if (lsp->body)
memcpy(pkt+26, lsp->body, lsp->blen);
ISIS_TRACE(D_PACKETS, "Sending LSP via %s", ifa->iface->name);
// xxx sk_send_to(ifa->sk, ifa->plen, AllNodes, 0);
}
void
isis_process_lsp(struct isis_iface *ifa, int level, byte *pkt, int len)
{
struct isis_proto *p = ifa->p;
if ((pkt[ISIS_HLEN_POS] != ISIS_LSP_HLEN) || (pkt[8] != len))
XXX;
put_u16(pkt+ 8, ISIS_LSP_HLEN - 1 + lsp->blen);
get_lsp_hdr(pkt+10, &hdr);
isis_lsp_received(db, ifa, &hdr, pkt+26, len-26)
if (lsp->body)
memcpy(pkt+26, lsp->body, lsp->blen);
}
static byte *
isis_put_tlv_lsp_entries(struct isis_iface *ifa, byte *buf, byte *be,
struct isis_lsdb *db, struct isis_lsp **lspp, int psnp)
{
struct isis_lsp *lsp = *lspp;
byte *bp = buf + 2;
int i = 0;
be -= 2 + 16; /* TLV header + sizeof(struct isis_lsp_hdr) */
while (lsp && bp <= be)
{
if (i == 15)
{
buf[0] = ISIS_TLV_LSP_ENTRIES;
buf[1] = i * 16;
i = 0;
buf = bp;
bp += 2;
}
bp = put_lsp_hdr(bp, &lsp->hdr);
if (psnp)
{
// XXX check
isis_lsdb_clear_ssn(db, lsp, ifa);
lsp = isis_lsdb_next_ssn(db, lsp, ifa);
}
else
lsp = isis_lsdb_next(db, lsp);
}
buf[0] = ISIS_TLV_LSP_ENTRIES;
buf[1] = i * 16;
*lspp = lsp;
}
void
isis_send_csnp(struct isis_iface *ifa, int level, struct isis_lsp **lspp, int first)
{
struct isis_proto *p = ifa->p;
byte *pkt = isis_tx_buffer(ifa);
byte *bp = pkt + ISIS_CSNP_HLEN;
byte *be = isis_tx_buffer_end(ifa);
/* First put TLVs */
u64 start_id = first ? ISIS_MIN_LSP_ID : (*lspp)->hdr.id;
bp = isis_put_tlv_lsp_entries(p, bp, be, p->lsdb[level], lspp, 0);
u64 end_id = *lspp ? (*lspp)->hdr.id - 1 : ISIS_MAX_LSP_ID;
/* Then put header */
isis_fill_hdr(pkt, !level ? ISIS_L1_CSNP : ISIS_L2_CSNP, ISIS_CSNP_HLEN);
put_u16(pkt+08, bp - pkt);
put_id7(pkt+10, p->system_id);
put_u64(pkt+17, start_id);
put_u64(pkt+25, end_id);
ISIS_TRACE(D_PACKETS, "Sending CSNP via %s", ifa->iface->name);
// xxx sk_send_to(ifa->sk, ifa->plen, AllNodes, 0);
}
void
isis_send_psnp(struct isis_iface *ifa, int level, struct isis_lsp **lspp)
{
struct isis_proto *p = ifa->p;
byte *pkt = isis_tx_buffer(ifa);
byte *bp = pkt + ISIS_PSNP_HLEN;
byte *be = isis_tx_buffer_end(ifa);
/* First put TLVs */
bp = isis_put_tlv_lsp_entries(p, bp, be, p->lsdb[level], lspp, 1);
/* Then put header */
isis_fill_hdr(pkt, !level ? ISIS_L1_PSNP : ISIS_L2_PSNP, ISIS_PSNP_HLEN);
put_u16(pkt+08, bp - pkt);
put_id7(pkt+10, p->system_id);
ISIS_TRACE(D_PACKETS, "Sending PSNP via %s", ifa->iface->name);
// xxx sk_send_to(ifa->sk, ifa->plen, AllNodes, 0);
}
static inline void
isis_process_tlv_lsp_entries(struct isis_iface *ifa, byte *tlv, byte *te, struct isis_lsdb *db)
{
struct isis_proto *p = ifa->p;
struct isis_lsp_hdr hdr;
byte *bp = tlv + 2;
while (bp + 16 <= te)
{
bp = get_lsp_hdr(bp, &hdr);
isis_snp_received(db, ifa, &hdr);
}
if (bp < te)
XXX;
}
static void
isis_process_csnp(struct isis_iface *ifa, int level, byte *pkt, int len)
{
struct isis_proto *p = ifa->p;
if ((pkt[ISIS_HLEN_POS] != ISIS_CSNP_HLEN) || (pkt[8] != len))
XXX;
u64 start_id = get_u64(pkt+17);
u64 end_id = get_u64(pkt+25);
XXX;
byte *pe = pkt + len;
byte *tlv = pkt + ISIS_CSNP_HLEN;
while (tlv < pe)
{
byte *te = tlv + 2 + tlv[1];
if (te > pe)
XXX;
if (tlv[0] == ISIS_TLV_LSP_ENTRIES)
isis_process_tlv_lsp_entries(ifa, tlv, te, p->lsdb[level]);
tlv = te;
}
}
static void
isis_process_psnp(struct isis_iface *ifa, int level, byte *pkt, int len)
{
struct isis_proto *p = ifa->p;
if ((pkt[ISIS_HLEN_POS] != ISIS_PSNP_HLEN) || (pkt[8] != len))
XXX;
byte *pe = pkt + len;
byte *tlv = pkt + ISIS_PSNP_HLEN;
while (tlv < pe)
{
byte *te = tlv + 2 + tlv[1];
if (te > pe)
XXX;
if (tlv[0] == ISIS_TLV_LSP_ENTRIES)
isis_process_tlv_lsp_entries(ifa, tlv, te, p->lsdb[level]);
tlv = te;
}
}
#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
static int
isis_rx_hook(sock *sk, int len)
{
struct isis_iface *ifa = sk->data;
struct isis_proto *p = ifa->p;
const char *err_dsc = NULL;
unsigned err_val = 0;
if (sk->lifindex != sk->iface->index)
return 1;
DBG("ISIS: RX hook called (iface %s, src %I, dst %I)\n",
sk->iface->name, sk->faddr, sk->laddr); // XXX
// XXX check src addr
// XXX skip eth header
byte *pkt = ip_skip_header(sk->rbuf, &len);
if (pkt == NULL)
DROP("too short", len);
if ((len < ISIS_COMMON_HLEN) || (len < pkt[ISIS_HLEN_POS]))
DROP("too short", len);
if (len > sk->rbsize) // XXX
DROP("too large", len);
if (pkt[0] != ISIS_PROTO_ID)
DROP("protocol ID mismatch", pkt[0]);
if (pkt[2] != 1)
DROP("version1 mismatch", pkt[2]);
if (pkt[3] != 0 && pkt[3] != 6)
DROP("id_length mismatch", pkt[3]);
if (pkt[5] != 1)
DROP("version2 mismatch", pkt[5]);
if (pkt[7] != 0 && pkt[7] != 3)
DROP("max_area_addrs mismatch", pkt[7]);
// XXX find neighbor?
int type = pkt[4] & ISIS_TYPE_MASK;
int level = ISIS_L1;
switch (type)
{
// XXX ptp
// case ISIS_PTP_HELLO:
case ISIS_L2_HELLO:
level = ISIS_L2;
case ISIS_L1_HELLO:
ISIS_TRACE(D_PACKETS, "Received Hello from xxx via %s", ifa->iface->name);
isis_lan_hello_rx(pkt, ifa, n, level);
break;
case ISIS_L2_LSP:
level = ISIS_L2;
case ISIS_L1_LSP:
ISIS_TRACE(D_PACKETS, "Received LSP from xxx via %s", ifa->iface->name);
isis_lsp_rx(pkt, ifa, n, level);
break;
case ISIS_L2_CSNP:
level = ISIS_L2;
case ISIS_L1_CSNP:
ISIS_TRACE(D_PACKETS, "Received CSNP from xxx via %s", ifa->iface->name);
isis_process_csnp(pkt, ifa, n, level);
break;
case ISIS_L2_PSNP:
level = ISIS_L2;
case ISIS_L1_PSNP:
ISIS_TRACE(D_PACKETS, "Received PSNP from xxx via %s", ifa->iface->name);
isis_psnp_received(pkt, ifa, n, level);
break;
default:
DROP("unknown type", type);
};
return 1;
drop:
log(L_WARN "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val);
return 1;
}
static void
isis_tx_hook(sock *sk)
{
struct isis_iface *ifa = sk->data;
log(L_WARN "%s: TX hook called", ifa->p->p.name);
}
static void
isis_err_hook(sock *sk, int err)
{
struct isis_iface *ifa = sk->data;
log(L_ERR "%s: Socket error: %m", ifa->p->p.name, err);
}
int
isis_sk_open(struct isis_iface *ifa)
{
sock *sk = sk_new(ifa->pool);
sk->type = SK_IP;
sk->dport = ISIS_PROTO;
sk->saddr = IPA_NONE;
sk->ttl = 0;
sk->rx_hook = isis_rx_hook;
sk->tx_hook = isis_tx_hook;
sk->err_hook = isis_err_hook;
sk->iface = ifa->iface;
sk->rbsize = p->rx_buffer_size + 64; // XXX
sk->tbsize = p->tx_buffer_size + 64; // XXX
sk->data = ifa;
sk->flags = SKF_LADDR_RX;
if (sk_open(sk) != 0)
goto err;
sk->saddr = ifa->addr->ip;
if (sk_setup_multicast(sk) < 0)
goto err;
if (sk_join_group(sk, AllRouters) < 0)
goto err;
ifa->sk = sk;
return 1;
err:
rfree(sk);
return 0;
}

View File

@ -41,6 +41,7 @@
#undef CONFIG_RADV
#undef CONFIG_BGP
#undef CONFIG_OSPF
#undef CONFIG_ISIS
#undef CONFIG_PIPE
/* We have <syslog.h> and syslog() */