mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Some preliminary IS-IS commit.
This commit is contained in:
parent
8ecbaf9c70
commit
fed1bceb2c
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
5
proto/isis/Makefile
Normal 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
187
proto/isis/config.Y
Normal 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
354
proto/isis/ifaces.c
Normal 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
124
proto/isis/isis.c
Normal 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
206
proto/isis/isis.h
Normal 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
89
proto/isis/lsdb.c
Normal 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
541
proto/isis/packets.c
Normal 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;
|
||||
}
|
||||
|
@ -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() */
|
||||
|
Loading…
Reference in New Issue
Block a user