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

Merge branch 'master' into birdtest

This commit is contained in:
Pavel Tvrdík 2015-05-13 11:41:03 +02:00
commit 00572d96f9
15 changed files with 684 additions and 224 deletions

View File

@ -2248,7 +2248,7 @@ these attributes:
<tag>ip <cf/krt_prefsrc/</tag> (Linux)
The preferred source address. Used in source address selection for
outgoing packets. Have to be one of IP addresses of the router.
outgoing packets. Has to be one of the IP addresses of the router.
<tag>int <cf/krt_realm/</tag> (Linux)
The realm of the route. Can be used for traffic classification.

View File

@ -516,6 +516,9 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
#define ACCESS_RTE \
do { if (!f_rte) runtime("No route to access"); } while (0)
#define BITFIELD_MASK(what) \
(1u << (what->a2.i >> 24))
/**
* interpret
* @what: filter to interpret
@ -864,12 +867,14 @@ interpret(struct f_inst *what)
ACCESS_RTE;
{
eattr *e = NULL;
u16 code = what->a2.i;
if (!(f_flags & FF_FORCE_TMPATTR))
e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i );
e = ea_find((*f_rte)->attrs->eattrs, code);
if (!e)
e = ea_find( (*f_tmp_attrs), what->a2.i );
e = ea_find((*f_tmp_attrs), code);
if ((!e) && (f_flags & FF_FORCE_TMPATTR))
e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i );
e = ea_find((*f_rte)->attrs->eattrs, code);
if (!e) {
/* A special case: undefined int_set looks like empty int_set */
@ -878,8 +883,9 @@ interpret(struct f_inst *what)
res.val.ad = adata_empty(f_pool, 0);
break;
}
/* The same special case for ec_set */
else if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) {
if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) {
res.type = T_ECLIST;
res.val.ad = adata_empty(f_pool, 0);
break;
@ -912,6 +918,10 @@ interpret(struct f_inst *what)
res.type = T_PATH;
res.val.ad = e->u.ptr;
break;
case EAF_TYPE_BITFIELD:
res.type = T_BOOL;
res.val.i = !!(e->u.data & BITFIELD_MASK(what));
break;
case EAF_TYPE_INT_SET:
res.type = T_CLIST;
res.val.ad = e->u.ptr;
@ -933,13 +943,15 @@ interpret(struct f_inst *what)
ONEARG;
{
struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr));
u16 code = what->a2.i;
l->next = NULL;
l->flags = EALF_SORTED;
l->count = 1;
l->attrs[0].id = what->a2.i;
l->attrs[0].id = code;
l->attrs[0].flags = 0;
l->attrs[0].type = what->aux | EAF_ORIGINATED;
switch (what->aux & EAF_TYPE_MASK) {
case EAF_TYPE_INT:
if (v1.type != T_INT)
@ -978,6 +990,26 @@ interpret(struct f_inst *what)
runtime( "Setting path attribute to non-path value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_BITFIELD:
if (v1.type != T_BOOL)
runtime( "Setting bit in bitfield attribute to non-bool value" );
{
/* First, we have to find the old value */
eattr *e = NULL;
if (!(f_flags & FF_FORCE_TMPATTR))
e = ea_find((*f_rte)->attrs->eattrs, code);
if (!e)
e = ea_find((*f_tmp_attrs), code);
if ((!e) && (f_flags & FF_FORCE_TMPATTR))
e = ea_find((*f_rte)->attrs->eattrs, code);
u32 data = e ? e->u.data : 0;
if (v1.val.i)
l->attrs[0].u.data = data | BITFIELD_MASK(what);
else
l->attrs[0].u.data = data & ~BITFIELD_MASK(what);;
}
break;
case EAF_TYPE_INT_SET:
if (v1.type != T_CLIST)
runtime( "Setting clist attribute to non-clist value" );

196
lib/fletcher16.h Normal file
View File

@ -0,0 +1,196 @@
/*
* BIRD Library -- Fletcher-16 checksum
*
* (c) 2015 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
* DOC: Fletcher-16 checksum
*
* Fletcher-16 checksum is a position-dependent checksum algorithm used for
* error-detection e.g. in OSPF LSAs.
*
* To generate Fletcher-16 checksum, zero the checksum field in data, initialize
* the context by fletcher16_init(), process the data by fletcher16_update(),
* compute the checksum value by fletcher16_final() and store it to the checksum
* field in data by put_u16() (or other means involving htons() conversion).
*
* To verify Fletcher-16 checksum, initialize the context by fletcher16_init(),
* process the data by fletcher16_update(), compute a passing checksum by
* fletcher16_compute() and check if it is zero.
*/
#ifndef _BIRD_FLETCHER16_H_
#define _BIRD_FLETCHER16_H_
#include "nest/bird.h"
struct fletcher16_context
{
int c0, c1;
};
/**
* fletcher16_init - initialize Fletcher-16 context
* @ctx: the context
*/
static inline void
fletcher16_init(struct fletcher16_context *ctx)
{
ctx->c0 = ctx->c1 = 0;
}
/**
* fletcher16_update - process data to Fletcher-16 context
* @ctx: the context
* @buf: data buffer
* @len: data length
*
* fletcher16_update() reads data from the buffer @buf and updates passing sums
* in the context @ctx. It may be used multiple times for multiple blocks of
* checksummed data.
*/
static inline void
fletcher16_update(struct fletcher16_context *ctx, const u8* buf, int len)
{
/*
* The Fletcher-16 sum is essentially a sequence of
* ctx->c1 += ctx->c0 += *buf++, modulo 255.
*
* In the inner loop, we eliminate modulo operation and we do some loop
* unrolling. MODX is the maximal number of steps that can be done without
* modulo before overflow, see RFC 1008 for details. We use a bit smaller
* value to cover for initial steps due to loop unrolling.
*/
#define MODX 4096
int blen, i;
blen = len % 4;
len -= blen;
for (i = 0; i < blen; i++)
ctx->c1 += ctx->c0 += *buf++;
do {
blen = MIN(len, MODX);
len -= blen;
for (i = 0; i < blen; i += 4)
{
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
}
ctx->c0 %= 255;
ctx->c1 %= 255;
} while (len);
}
/**
* fletcher16_update_n32 - process data to Fletcher-16 context, with endianity adjustment
* @ctx: the context
* @buf: data buffer
* @len: data length
*
* fletcher16_update_n32() works like fletcher16_update(), except it applies
* 32-bit host/network endianity swap to the data before they are processed.
* I.e., it assumes that the data is a sequence of u32 that must be converted by
* ntohl() or htonl() before processing. The @buf need not to be aligned, but
* its length (@len) must be multiple of 4. Note that on big endian systems the
* host endianity is the same as the network endianity, therefore there is no
* endianity swap.
*/
static inline void
fletcher16_update_n32(struct fletcher16_context *ctx, const u8* buf, int len)
{
/* See fletcher16_update() for details */
int blen, i;
do {
blen = MIN(len, MODX);
len -= blen;
for (i = 0; i < blen; i += 4)
{
#ifdef CPU_BIG_ENDIAN
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
#else
ctx->c1 += ctx->c0 += buf[3];
ctx->c1 += ctx->c0 += buf[2];
ctx->c1 += ctx->c0 += buf[1];
ctx->c1 += ctx->c0 += buf[0];
buf += 4;
#endif
}
ctx->c0 %= 255;
ctx->c1 %= 255;
} while (len);
}
/**
* fletcher16_final - compute final Fletcher-16 checksum value
* @ctx: the context
* @len: total data length
* @pos: offset in data where the checksum will be stored
*
* fletcher16_final() computes the final checksum value and returns it.
* The caller is responsible for storing it in the appropriate position.
* The checksum value depends on @len and @pos, but only their difference
* (i.e. the offset from the end) is significant.
*
* The checksum value is represented as u16, although it is defined as two
* consecutive bytes. We treat them as one u16 in big endian / network order.
* I.e., the returned value is in the form that would be returned by get_u16()
* from the checksum field in the data buffer, therefore the caller should use
* put_u16() or an explicit host-to-network conversion when storing it to the
* checksum field in the data buffer.
*
* Note that the returned checksum value is always nonzero.
*/
static inline u16
fletcher16_final(struct fletcher16_context *ctx, int len, int pos)
{
int x = ((len - pos - 1) * ctx->c0 - ctx->c1) % 255;
if (x <= 0)
x += 255;
int y = 510 - ctx->c0 - x;
if (y > 255)
y -= 255;
return (x << 8) | y;
}
/**
* fletcher16_compute - compute Fletcher-16 sum for verification
* @ctx: the context
*
* fletcher16_compute() returns a passing Fletcher-16 sum for processed data.
* If the data contains the proper Fletcher-16 checksum value, the returned
* value is zero.
*/
static inline u16
fletcher16_compute(struct fletcher16_context *ctx)
{
return (ctx->c0 << 8) | ctx->c1;
}
#endif

View File

@ -417,13 +417,15 @@ typedef struct eattr {
#define EA_CODE_MASK 0xffff
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */
#define EAF_TYPE_MASK 0x0f /* Mask with this to get type */
#define EAF_TYPE_INT 0x01 /* 32-bit signed integer number */
#define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */
#define EAF_TYPE_OPAQUE 0x02 /* Opaque byte string (not filterable) */
#define EAF_TYPE_IP_ADDRESS 0x04 /* IP address */
#define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */
#define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */
#define EAF_TYPE_BITFIELD 0x09 /* 32-bit embedded bitfield */
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
#define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */
#define EAF_TYPE_UNDEF 0x0f /* `force undefined' entry */
@ -459,8 +461,14 @@ static inline void rt_lock_source(struct rte_src *src) { src->uc++; }
static inline void rt_unlock_source(struct rte_src *src) { src->uc--; }
void rt_prune_sources(void);
struct ea_walk_state {
ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */
eattr *ea; /* Current eattr, initially NULL */
u32 visited[4]; /* Bitfield, limiting max to 128 */
};
eattr *ea_find(ea_list *, unsigned ea);
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
int ea_get_int(ea_list *, unsigned ea, int def);
void ea_dump(ea_list *);
void ea_sort(ea_list *); /* Sort entries in all sub-lists */
@ -469,6 +477,7 @@ void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffe
int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */
unsigned int ea_hash(ea_list *e); /* Calculate 16-bit hash value */
ea_list *ea_append(ea_list *to, ea_list *what);
void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */
static inline int mpnh_same(struct mpnh *x, struct mpnh *y)

View File

@ -307,6 +307,82 @@ ea_find(ea_list *e, unsigned id)
return a;
}
/**
* ea_walk - walk through extended attributes
* @s: walk state structure
* @id: start of attribute ID interval
* @max: length of attribute ID interval
*
* Given an extended attribute list, ea_walk() walks through the list looking
* for first occurrences of attributes with ID in specified interval from @id to
* (@id + @max - 1), returning pointers to found &eattr structures, storing its
* walk state in @s for subsequent calls.
* The function ea_walk() is supposed to be called in a loop, with initially
* zeroed walk state structure @s with filled the initial extended attribute
* list, returning one found attribute in each call or %NULL when no other
* attribute exists. The extended attribute list or the arguments should not be
* modified between calls. The maximum value of @max is 128.
*/
eattr *
ea_walk(struct ea_walk_state *s, uint id, uint max)
{
ea_list *e = s->eattrs;
eattr *a = s->ea;
eattr *a_max;
max = id + max;
if (a)
goto step;
for (; e; e = e->next)
{
if (e->flags & EALF_BISECT)
{
int l, r, m;
l = 0;
r = e->count - 1;
while (l < r)
{
m = (l+r) / 2;
if (e->attrs[m].id < id)
l = m + 1;
else
r = m;
}
a = e->attrs + l;
}
else
a = e->attrs;
step:
a_max = e->attrs + e->count;
for (; a < a_max; a++)
if ((a->id >= id) && (a->id < max))
{
int n = a->id - id;
if (BIT32_TEST(s->visited, n))
continue;
BIT32_SET(s->visited, n);
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
continue;
s->eattrs = e;
s->ea = a;
return a;
}
else if (e->flags & EALF_BISECT)
break;
}
return NULL;
}
/**
* ea_get_int - fetch an integer attribute
* @e: attribute list
@ -563,6 +639,32 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
return GA_UNKNOWN;
}
void
ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max)
{
byte *bound = buf + bufsize - 32;
u32 data = a->u.data;
int i;
for (i = min; i < max; i++)
if ((data & (1u << i)) && names[i])
{
if (buf > bound)
{
strcpy(buf, " ...");
return;
}
buf += bsprintf(buf, " %s", names[i]);
data &= ~(1u << i);
}
if (data)
bsprintf(buf, " %08x", data);
return;
}
static inline void
opaque_format(struct adata *ad, byte *buf, unsigned int size)
{
@ -665,6 +767,9 @@ ea_show(struct cli *c, eattr *e)
case EAF_TYPE_AS_PATH:
as_path_format(ad, pos, end - pos);
break;
case EAF_TYPE_BITFIELD:
bsprintf(pos, "%08x", e->u.data);
break;
case EAF_TYPE_INT_SET:
ea_show_int_set(c, ad, 1, pos, buf, end);
return;

View File

@ -2,14 +2,15 @@
* BIRD -- OSPF
*
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
* (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2014 CZ.NIC z.s.p.o.
* (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "ospf.h"
#include "lib/fletcher16.h"
#ifndef CPU_BIG_ENDIAN
void
@ -150,144 +151,40 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
}
/*
void
buf_dump(const char *hdr, const byte *buf, int blen)
lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body)
{
char b2[1024];
char *bp;
int first = 1;
int i;
const char *lhdr = hdr;
bp = b2;
for(i = 0; i < blen; i++)
{
if ((i > 0) && ((i % 16) == 0))
{
*bp = 0;
log(L_WARN "%s\t%s", lhdr, b2);
lhdr = "";
bp = b2;
}
bp += snprintf(bp, 1022, "%02x ", buf[i]);
}
*bp = 0;
log(L_WARN "%s\t%s", lhdr, b2);
}
*/
#define MODX 4102 /* larges signed value without overflow */
/* Fletcher Checksum -- Refer to RFC1008. */
#define MODX 4102
#define LSA_CHECKSUM_OFFSET 15
/* FIXME This is VERY uneficient, I have huge endianity problems */
void
lsasum_calculate(struct ospf_lsa_header *h, void *body)
{
u16 length = h->length;
// log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length);
lsa_hton_hdr(h, h);
lsa_hton_body1(body, length - sizeof(struct ospf_lsa_header));
struct fletcher16_context ctx;
struct ospf_lsa_header hdr;
u16 len = lsa->length;
/*
char buf[1024];
memcpy(buf, h, sizeof(struct ospf_lsa_header));
memcpy(buf + sizeof(struct ospf_lsa_header), body, length - sizeof(struct ospf_lsa_header));
buf_dump("CALC", buf, length);
* lsa and body are in the host order, we need to compute Fletcher-16 checksum
* for data in the network order. We also skip the initial age field.
*/
(void) lsasum_check(h, body, 1);
lsa_hton_hdr(lsa, &hdr);
hdr.checksum = 0;
// log(L_WARN "Checksum result %4x", h->checksum);
lsa_ntoh_hdr(h, h);
lsa_ntoh_body1(body, length - sizeof(struct ospf_lsa_header));
fletcher16_init(&ctx);
fletcher16_update(&ctx, (u8 *) &hdr + 2, sizeof(struct ospf_lsa_header) - 2);
fletcher16_update_n32(&ctx, body, len - sizeof(struct ospf_lsa_header));
lsa->checksum = fletcher16_final(&ctx, len, OFFSETOF(struct ospf_lsa_header, checksum));
}
/*
* Calculates the Fletcher checksum of an OSPF LSA.
*
* If 'update' is non-zero, the checkbytes (X and Y in RFC905) are calculated
* and the checksum field in the header is updated. The return value is the
* checksum as placed in the header (in network byte order).
*
* If 'update' is zero, only C0 and C1 are calculated and the header is kept
* intact. The return value is a combination of C0 and C1; if the return value
* is exactly zero the checksum is considered valid, any non-zero value is
* invalid.
*
* Note that this function expects the input LSA to be in network byte order.
*/
u16
lsasum_check(struct ospf_lsa_header *h, void *body, int update)
lsa_verify_checksum(const void *lsa_n, int lsa_len)
{
u8 *sp, *ep, *p, *q, *b;
int c0 = 0, c1 = 0;
int x, y;
u16 length;
struct fletcher16_context ctx;
b = body;
sp = (char *) h;
sp += 2; /* Skip Age field */
length = ntohs(h->length) - 2;
if (update) h->checksum = 0;
/* The whole LSA is at lsa_n in net order, we just skip initial age field */
for (ep = sp + length; sp < ep; sp = q)
{ /* Actually MODX is very large, do we need the for-cyclus? */
q = sp + MODX;
if (q > ep)
q = ep;
for (p = sp; p < q; p++)
{
/*
* I count with bytes from header and than from body
* but if there is no body, it's appended to header
* (probably checksum in update receiving) and I go on
* after header
*/
if ((b == NULL) || (p < (u8 *) (h + 1)))
{
c0 += *p;
}
else
{
c0 += *(b + (p - (u8 *) (h + 1)));
fletcher16_init(&ctx);
fletcher16_update(&ctx, (u8 *) lsa_n + 2, lsa_len - 2);
return fletcher16_compute(&ctx) == 0;
}
c1 += c0;
}
c0 %= 255;
c1 %= 255;
}
if (!update) {
/*
* When testing the checksum, we don't need to calculate x and y. The
* checksum passes if c0 and c1 are both 0.
*/
return (c0 << 8) | (c1 & 0xff);
}
x = (int)((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
if (x <= 0)
x += 255;
y = 510 - c0 - x;
if (y > 255)
y -= 255;
((u8 *) & h->checksum)[0] = x;
((u8 *) & h->checksum)[1] = y;
return h->checksum;
}
int
lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)

View File

@ -46,9 +46,9 @@ static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
void lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body);
u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
void lsasum_calculate(struct ospf_lsa_header *header, void *body);
u16 lsasum_check(struct ospf_lsa_header *h, void *body, int update);
#define CMP_NEWER 1
#define CMP_SAME 0
#define CMP_OLDER -1

View File

@ -530,8 +530,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n",
lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum);
/* RFC 2328 13. (1) - validate LSA checksum */
if ((lsa_n->checksum == 0) || (lsasum_check(lsa_n, NULL, 0) != 0))
/* RFC 2328 13. (1) - verify LSA checksum */
if ((lsa_n->checksum == 0) || !lsa_verify_checksum(lsa_n, lsa_len))
SKIP("invalid checksum");
/* RFC 2328 13. (2) */

View File

@ -129,7 +129,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = now;
lsasum_calculate(&en->lsa, en->lsa_body);
lsa_generate_checksum(&en->lsa, en->lsa_body);
OSPF_TRACE(D_EVENTS, "Advancing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
@ -238,7 +238,7 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = now;
lsasum_calculate(&en->lsa, en->lsa_body);
lsa_generate_checksum(&en->lsa, en->lsa_body);
OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
@ -382,7 +382,7 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en)
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = now;
lsasum_calculate(&en->lsa, en->lsa_body);
lsa_generate_checksum(&en->lsa, en->lsa_body);
ospf_flood_lsa(p, en, NULL);
}

View File

@ -44,5 +44,7 @@ struct krt_state {
static inline void krt_sys_init(struct krt_proto *p UNUSED) { }
static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { }
#endif

View File

@ -32,6 +32,59 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; }
/* Kernel routes */
#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0x10)
#define EA_KRT_REALM EA_CODE(EAP_KRT, 0x11)
#define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */
#define KRT_METRICS_OFFSET 0x20 /* Offset of EA_KRT_* vs RTAX_* */
#define KRT_FEATURES_MAX 4
/*
* Following attributes are parts of RTA_METRICS kernel route attribute, their
* ids must be consistent with their RTAX_* constants (+ KRT_METRICS_OFFSET)
*/
#define EA_KRT_METRICS EA_CODE(EAP_KRT, 0x20) /* Dummy one */
#define EA_KRT_LOCK EA_CODE(EAP_KRT, 0x21)
#define EA_KRT_MTU EA_CODE(EAP_KRT, 0x22)
#define EA_KRT_WINDOW EA_CODE(EAP_KRT, 0x23)
#define EA_KRT_RTT EA_CODE(EAP_KRT, 0x24)
#define EA_KRT_RTTVAR EA_CODE(EAP_KRT, 0x25)
#define EA_KRT_SSTRESH EA_CODE(EAP_KRT, 0x26)
#define EA_KRT_CWND EA_CODE(EAP_KRT, 0x27)
#define EA_KRT_ADVMSS EA_CODE(EAP_KRT, 0x28)
#define EA_KRT_REORDERING EA_CODE(EAP_KRT, 0x29)
#define EA_KRT_HOPLIMIT EA_CODE(EAP_KRT, 0x2a)
#define EA_KRT_INITCWND EA_CODE(EAP_KRT, 0x2b)
#define EA_KRT_FEATURES EA_CODE(EAP_KRT, 0x2c)
#define EA_KRT_RTO_MIN EA_CODE(EAP_KRT, 0x2d)
#define EA_KRT_INITRWND EA_CODE(EAP_KRT, 0x2e)
#define EA_KRT_QUICKACK EA_CODE(EAP_KRT, 0x2f)
/* Bits of EA_KRT_LOCK, also based on RTAX_* constants */
#define EA_KRT_LOCK_MTU EA_KRT_LOCK | EA_BIT(0x2)
#define EA_KRT_LOCK_WINDOW EA_KRT_LOCK | EA_BIT(0x3)
#define EA_KRT_LOCK_RTT EA_KRT_LOCK | EA_BIT(0x4)
#define EA_KRT_LOCK_RTTVAR EA_KRT_LOCK | EA_BIT(0x5)
#define EA_KRT_LOCK_SSTHRESH EA_KRT_LOCK | EA_BIT(0x6)
#define EA_KRT_LOCK_CWND EA_KRT_LOCK | EA_BIT(0x7)
#define EA_KRT_LOCK_ADVMSS EA_KRT_LOCK | EA_BIT(0x8)
#define EA_KRT_LOCK_REORDERING EA_KRT_LOCK | EA_BIT(0x9)
#define EA_KRT_LOCK_HOPLIMIT EA_KRT_LOCK | EA_BIT(0xa)
// define EA_KRT_LOCK_INITCWND EA_KRT_LOCK | EA_BIT(0xb)
// define EA_KRT_LOCK_FEATURES EA_KRT_LOCK | EA_BIT(0xc)
#define EA_KRT_LOCK_RTO_MIN EA_KRT_LOCK | EA_BIT(0xd)
// define EA_KRT_LOCK_INITRWND EA_KRT_LOCK | EA_BIT(0xe)
/* Bits of EA_KRT_FEATURES, based on RTAX_FEATURE_* constants */
#define EA_KRT_FEATURE_ECN EA_KRT_FEATURES | EA_BIT(0x0)
// define EA_KRT_FEATURE_SACK EA_KRT_FEATURES | EA_BIT(0x1)
// define EA_KRT_FEATURE_TSTAMP EA_KRT_FEATURES | EA_BIT(0x2)
#define EA_KRT_FEATURE_ALLFRAG EA_KRT_FEATURES | EA_BIT(0x3)
#define NL_NUM_TABLES 256
struct krt_params {

View File

@ -10,7 +10,12 @@ CF_HDR
CF_DECLS
CF_KEYWORDS(KERNEL, TABLE, KRT_PREFSRC, KRT_REALM)
CF_KEYWORDS(KERNEL, TABLE, KRT_PREFSRC, KRT_REALM, KRT_MTU, KRT_WINDOW, KRT_RTT,
KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING,
KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK,
KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR,
KRT_LOCK_SSTRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING,
KRT_LOCK_HOPLIMIT, KRT_LOCK_RTO_MIN, KRT_FEATURE_ECN, KRT_FEATURE_ALLFRAG)
CF_GRAMMAR
@ -27,6 +32,35 @@ kern_sys_item:
CF_ADDTO(dynamic_attr, KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); })
CF_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); })
CF_ADDTO(dynamic_attr, KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU); })
CF_ADDTO(dynamic_attr, KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); })
CF_ADDTO(dynamic_attr, KRT_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTT); })
CF_ADDTO(dynamic_attr, KRT_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTTVAR); })
CF_ADDTO(dynamic_attr, KRT_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SSTRESH); })
CF_ADDTO(dynamic_attr, KRT_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_CWND); })
CF_ADDTO(dynamic_attr, KRT_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_ADVMSS); })
CF_ADDTO(dynamic_attr, KRT_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REORDERING); })
CF_ADDTO(dynamic_attr, KRT_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_HOPLIMIT); })
CF_ADDTO(dynamic_attr, KRT_INITCWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITCWND); })
CF_ADDTO(dynamic_attr, KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTO_MIN); })
CF_ADDTO(dynamic_attr, KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITRWND); })
CF_ADDTO(dynamic_attr, KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_QUICKACK); })
CF_ADDTO(dynamic_attr, KRT_LOCK_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_MTU); })
CF_ADDTO(dynamic_attr, KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_WINDOW); })
CF_ADDTO(dynamic_attr, KRT_LOCK_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTT); })
CF_ADDTO(dynamic_attr, KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTTVAR); })
CF_ADDTO(dynamic_attr, KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_SSTHRESH); })
CF_ADDTO(dynamic_attr, KRT_LOCK_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_CWND); })
CF_ADDTO(dynamic_attr, KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_ADVMSS); })
CF_ADDTO(dynamic_attr, KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_REORDERING); })
CF_ADDTO(dynamic_attr, KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_HOPLIMIT); })
CF_ADDTO(dynamic_attr, KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTO_MIN); })
CF_ADDTO(dynamic_attr, KRT_FEATURE_ECN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ECN); })
CF_ADDTO(dynamic_attr, KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ALLFRAG); })
CF_CODE
CF_END

View File

@ -238,21 +238,24 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize)
return 1;
}
void
nl_add_attr(struct nlmsghdr *h, unsigned bufsize, unsigned code,
void *data, unsigned dlen)
struct rtattr *
nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen)
{
unsigned len = RTA_LENGTH(dlen);
unsigned pos = NLMSG_ALIGN(h->nlmsg_len);
struct rtattr *a;
uint pos = NLMSG_ALIGN(h->nlmsg_len);
uint len = RTA_LENGTH(dlen);
if (pos + len > bufsize)
bug("nl_add_attr: packet buffer overflow");
a = (struct rtattr *)((char *)h + pos);
struct rtattr *a = (struct rtattr *)((char *)h + pos);
a->rta_type = code;
a->rta_len = len;
h->nlmsg_len = pos + len;
if (dlen > 0)
memcpy(RTA_DATA(a), data, dlen);
return a;
}
static inline void
@ -268,49 +271,59 @@ nl_add_attr_ipa(struct nlmsghdr *h, unsigned bufsize, int code, ip_addr ipa)
nl_add_attr(h, bufsize, code, &ipa, sizeof(ipa));
}
#define RTNH_SIZE (sizeof(struct rtnexthop) + sizeof(struct rtattr) + sizeof(ip_addr))
static inline void
add_mpnexthop(char *buf, ip_addr ipa, unsigned iface, unsigned char weight)
static inline struct rtattr *
nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code)
{
struct rtnexthop *nh = (void *) buf;
struct rtattr *rt = (void *) (buf + sizeof(*nh));
nh->rtnh_len = RTNH_SIZE;
nh->rtnh_flags = 0;
nh->rtnh_hops = weight;
nh->rtnh_ifindex = iface;
rt->rta_len = sizeof(*rt) + sizeof(ipa);
rt->rta_type = RTA_GATEWAY;
ipa_hton(ipa);
memcpy(buf + sizeof(*nh) + sizeof(*rt), &ipa, sizeof(ipa));
return nl_add_attr(h, bufsize, code, NULL, 0);
}
static inline void
nl_close_attr(struct nlmsghdr *h, struct rtattr *a)
{
a->rta_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)a;
}
static inline struct rtnexthop *
nl_open_nexthop(struct nlmsghdr *h, uint bufsize)
{
uint pos = NLMSG_ALIGN(h->nlmsg_len);
uint len = RTNH_LENGTH(0);
if (pos + len > bufsize)
bug("nl_open_nexthop: packet buffer overflow");
h->nlmsg_len = pos + len;
return (void *)h + pos;
}
static inline void
nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh)
{
nh->rtnh_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)nh;
}
static void
nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh)
{
unsigned len = sizeof(struct rtattr);
unsigned pos = NLMSG_ALIGN(h->nlmsg_len);
char *buf = (char *)h + pos;
struct rtattr *rt = (void *) buf;
buf += len;
struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
for (; nh; nh = nh->next)
{
len += RTNH_SIZE;
if (pos + len > bufsize)
bug("nl_add_multipath: packet buffer overflow");
struct rtnexthop *rtnh = nl_open_nexthop(h, bufsize);
add_mpnexthop(buf, nh->gw, nh->iface->index, nh->weight);
buf += RTNH_SIZE;
rtnh->rtnh_flags = 0;
rtnh->rtnh_hops = nh->weight;
rtnh->rtnh_ifindex = nh->iface->index;
nl_add_attr_u32(h, bufsize, RTA_GATEWAY, nh->gw);
nl_close_nexthop(h, rtnh);
}
rt->rta_type = RTA_MULTIPATH;
rt->rta_len = len;
h->nlmsg_len = pos + len;
nl_close_attr(h, a);
}
static struct mpnh *
nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
{
@ -374,6 +387,47 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
return first;
}
static void
nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max)
{
struct rtattr *a = nl_open_attr(h, bufsize, RTA_METRICS);
int t;
for (t = 1; t < max; t++)
if (metrics[0] & (1 << t))
nl_add_attr_u32(h, bufsize, t, metrics[t]);
nl_close_attr(h, a);
}
static int
nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max)
{
struct rtattr *a = RTA_DATA(hdr);
int len = RTA_PAYLOAD(hdr);
metrics[0] = 0;
for (; RTA_OK(a, len); a = RTA_NEXT(a, len))
{
if (a->rta_type == RTA_UNSPEC)
continue;
if (a->rta_type >= max)
continue;
if (RTA_PAYLOAD(a) != 4)
return -1;
metrics[0] |= 1 << a->rta_type;
metrics[a->rta_type] = *(u32 *)RTA_DATA(a);
}
if (len > 0)
return -1;
return 0;
}
/*
* Scanning of interfaces
@ -617,7 +671,7 @@ nh_bufsize(struct mpnh *nh)
{
int rv = 0;
for (; nh != NULL; nh = nh->next)
rv += RTNH_SIZE;
rv += RTNH_LENGTH(RTA_LENGTH(sizeof(ip_addr)));
return rv;
}
@ -630,7 +684,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
struct {
struct nlmsghdr h;
struct rtmsg r;
char buf[128 + nh_bufsize(a->nexthops)];
char buf[128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops)];
} r;
DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new);
@ -649,13 +703,8 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
r.r.rtm_scope = RT_SCOPE_UNIVERSE;
nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
u32 metric = 0;
if (new && e->attrs->source == RTS_INHERIT)
metric = e->u.krt.metric;
if (ea = ea_find(eattrs, EA_KRT_METRIC))
metric = ea->u.data;
if (metric != 0)
nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric);
nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data);
if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
@ -663,6 +712,22 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
if (ea = ea_find(eattrs, EA_KRT_REALM))
nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
u32 metrics[KRT_METRICS_MAX];
metrics[0] = 0;
struct ea_walk_state ews = { .eattrs = eattrs };
while (ea = ea_walk(&ews, EA_KRT_METRICS, KRT_METRICS_MAX))
{
int id = ea->id - EA_KRT_METRICS;
metrics[0] |= 1 << id;
metrics[id] = ea->u.data;
}
if (metrics[0])
nl_add_metrics(&r.h, sizeof(r), metrics, KRT_METRICS_MAX);
/* a->iface != NULL checked in krt_capable() for router and device routes */
switch (a->dest)
@ -932,6 +997,38 @@ nl_parse_route(struct nlmsghdr *h, int scan)
memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4);
}
if (a[RTA_METRICS])
{
u32 metrics[KRT_METRICS_MAX];
ea_list *ea = alloca(sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr));
int t, n = 0;
if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0)
{
log(L_ERR "KRT: Received route %I/%d with strange RTA_METRICS attribute",
net->n.prefix, net->n.pxlen);
return;
}
for (t = 1; t < KRT_METRICS_MAX; t++)
if (metrics[0] & (1 << t))
{
ea->attrs[n].id = EA_CODE(EAP_KRT, KRT_METRICS_OFFSET + t);
ea->attrs[n].flags = 0;
ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */
ea->attrs[n].u.data = metrics[t];
n++;
}
if (n > 0)
{
ea->next = ra.eattrs;
ea->flags = EALF_SORTED;
ea->count = n;
ra.eattrs = ea;
}
}
if (scan)
krt_got_route(p, e);
else
@ -1130,6 +1227,50 @@ krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
d->sys.table_id = s->sys.table_id;
}
static const char *krt_metrics_names[KRT_METRICS_MAX] = {
NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss",
"reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack"
};
static const char *krt_features_names[KRT_FEATURES_MAX] = {
"ecn", NULL, NULL, "allfrag"
};
int
krt_sys_get_attr(eattr *a, byte *buf, int buflen UNUSED)
{
switch (a->id)
{
case EA_KRT_PREFSRC:
bsprintf(buf, "prefsrc");
return GA_NAME;
case EA_KRT_REALM:
bsprintf(buf, "realm");
return GA_NAME;
case EA_KRT_LOCK:
buf += bsprintf(buf, "lock:");
ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX);
return GA_FULL;
case EA_KRT_FEATURES:
buf += bsprintf(buf, "features:");
ea_format_bitfield(a, buf, buflen, krt_features_names, 0, KRT_FEATURES_MAX);
return GA_FULL;
default:;
int id = (int)EA_ID(a->id) - KRT_METRICS_OFFSET;
if (id > 0 && id < KRT_METRICS_MAX)
{
bsprintf(buf, "%s", krt_metrics_names[id]);
return GA_NAME;
}
return GA_UNKNOWN;
}
}
void

View File

@ -1191,7 +1191,7 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src)
}
static int
krt_get_attr(eattr * a, byte * buf, int buflen UNUSED)
krt_get_attr(eattr *a, byte *buf, int buflen)
{
switch (a->id)
{
@ -1203,16 +1203,8 @@ krt_get_attr(eattr * a, byte * buf, int buflen UNUSED)
bsprintf(buf, "metric");
return GA_NAME;
case EA_KRT_PREFSRC:
bsprintf(buf, "prefsrc");
return GA_NAME;
case EA_KRT_REALM:
bsprintf(buf, "realm");
return GA_NAME;
default:
return GA_UNKNOWN;
return krt_sys_get_attr(a, buf, buflen);
}
}

View File

@ -28,8 +28,6 @@ struct kif_proto;
#define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0)
#define EA_KRT_METRIC EA_CODE(EAP_KRT, 1)
#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 2)
#define EA_KRT_REALM EA_CODE(EAP_KRT, 3)
/* Whenever we recognize our own routes, we allow learing of foreign routes */
@ -131,6 +129,7 @@ void krt_sys_copy_config(struct krt_config *, struct krt_config *);
int krt_capable(rte *e);
void krt_do_scan(struct krt_proto *);
void krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs);
int krt_sys_get_attr(eattr *a, byte *buf, int buflen);
/* kif sysdep */