summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
Diffstat (limited to 'proto')
-rw-r--r--proto/bgp/attrs.c3
-rw-r--r--proto/bgp/bgp.c15
-rw-r--r--proto/bgp/bgp.h1
-rw-r--r--proto/bgp/config.Y4
-rw-r--r--proto/ospf/lsupd.c6
-rw-r--r--proto/ospf/ospf.c2
-rw-r--r--proto/ospf/rt.c4
-rw-r--r--proto/ospf/topology.c15
-rw-r--r--proto/ospf/topology.h2
-rw-r--r--proto/radv/config.Y126
-rw-r--r--proto/radv/packets.c181
-rw-r--r--proto/radv/radv.c4
-rw-r--r--proto/radv/radv.h31
13 files changed, 367 insertions, 27 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 4495c039..e5bc84dd 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -1258,7 +1258,8 @@ same_group(rte *r, u32 lpref, u32 lasn)
static inline int
use_deterministic_med(rte *r)
{
- return ((struct bgp_proto *) r->attrs->proto)->cf->deterministic_med;
+ struct proto *P = r->attrs->proto;
+ return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med;
}
int
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 3b9f7cc5..dbc59eea 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -870,6 +870,7 @@ bgp_shutdown(struct proto *P)
break;
case PDC_CMD_DISABLE:
+ case PDC_CMD_SHUTDOWN:
subcode = 2; // Errcode 6, 2 - administrative shutdown
break;
@@ -923,7 +924,7 @@ bgp_init(struct proto_config *C)
struct proto *P = proto_new(C, sizeof(struct bgp_proto));
struct bgp_proto *p = (struct bgp_proto *) P;
- P->accept_ra_types = RA_OPTIMAL;
+ P->accept_ra_types = c->secondary ? RA_ACCEPTED : RA_OPTIMAL;
P->rt_notify = bgp_rt_notify;
P->rte_better = bgp_rte_better;
P->import_control = bgp_import_control;
@@ -969,6 +970,7 @@ bgp_check_config(struct bgp_config *c)
if (internal && c->rs_client)
cf_error("Only external neighbor can be RS client");
+
if (c->multihop && (c->gw_mode == GW_DIRECT))
cf_error("Multihop BGP cannot use direct gateway mode");
@@ -976,6 +978,7 @@ bgp_check_config(struct bgp_config *c)
ipa_has_link_scope(c->source_addr)))
cf_error("Multihop BGP cannot be used with link-local addresses");
+
/* Different default based on rs_client */
if (!c->missing_lladdr)
c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
@@ -987,6 +990,16 @@ bgp_check_config(struct bgp_config *c)
/* Disable after error incompatible with restart limit action */
if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
c->c.in_limit->action = PLA_DISABLE;
+
+
+ if ((c->gw_mode == GW_RECURSIVE) && c->c.table->sorted)
+ cf_error("BGP in recursive mode prohibits sorted table");
+
+ if (c->deterministic_med && c->c.table->sorted)
+ cf_error("BGP with deterministic MED prohibits sorted table");
+
+ if (c->secondary && !c->c.table->sorted)
+ cf_error("BGP with secondary option requires sorted table");
}
static int
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 1c16f485..c3adf254 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -42,6 +42,7 @@ struct bgp_config {
int advertise_ipv4; /* Whether we should add IPv4 capability advertisement to OPEN message */
int passive; /* Do not initiate outgoing connection */
int interpret_communities; /* Hardwired handling of well-known communities */
+ int secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
unsigned connect_retry_time;
unsigned hold_time, initial_hold_time;
unsigned keepalive_time;
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 5feaea0d..8b80d7fd 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -25,7 +25,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
- TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC)
+ TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
+ SECONDARY)
CF_GRAMMAR
@@ -105,6 +106,7 @@ bgp_proto:
}
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
+ | bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
;
diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c
index f71c72d1..16967a7f 100644
--- a/proto/ospf/lsupd.c
+++ b/proto/ospf/lsupd.c
@@ -502,15 +502,17 @@ ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
continue;
}
#else /* OSPFv3 */
+ u16 scope = ntoht(lsa->type) & LSA_SCOPE_MASK;
+
/* 4.5.1 (2) */
- if ((LSA_SCOPE(lsa) == LSA_SCOPE_AS) && !oa_is_ext(ifa->oa))
+ if ((scope == LSA_SCOPE_AS) && !oa_is_ext(ifa->oa))
{
log(L_WARN "Received LSA with AS scope in stub area from %I", n->ip);
continue;
}
/* 4.5.1 (3) */
- if ((LSA_SCOPE(lsa) == LSA_SCOPE_RES))
+ if (scope == LSA_SCOPE_RES)
{
log(L_WARN "Received LSA with invalid scope from %I", n->ip);
continue;
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index ef7b0363..aa62da14 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -575,7 +575,7 @@ ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * ol
if (fn->x1 != EXT_EXPORT)
return;
- flush_ext_lsa(oa, fn);
+ flush_ext_lsa(oa, fn, oa_is_nssa(oa));
/* Old external route might blocked some NSSA translation */
if (po->areano > 1)
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 42c54dfd..4b8de4b8 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -1066,7 +1066,7 @@ check_nssa_lsa(struct proto_ospf *po, ort *nf)
originate_ext_lsa(po->backbone, fn, EXT_NSSA, rt_metric, rt_fwaddr, rt_tag, 0);
else if (fn->x1 == EXT_NSSA)
- flush_ext_lsa(po->backbone, fn);
+ flush_ext_lsa(po->backbone, fn, 0);
}
/* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
@@ -1189,7 +1189,7 @@ ospf_rt_abr1(struct proto_ospf *po)
if (oa_is_nssa(oa) && oa->ac->default_nssa)
originate_ext_lsa(oa, &default_nf->fn, 0, oa->ac->default_cost, IPA_NONE, 0, 0);
else
- flush_ext_lsa(oa, &default_nf->fn);
+ flush_ext_lsa(oa, &default_nf->fn, 1);
/* RFC 2328 16.4. (3) - precompute preferred ASBR entries */
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index 7e9bad50..ec012b22 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -862,6 +862,9 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL)
{
+ OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)",
+ en->lsa.id, en->lsa.type);
+
if ((type == ORT_NET) && check_sum_net_lsaid_collision(fn, en))
{
log(L_ERR "%s: LSAID collision for %I/%d",
@@ -873,9 +876,6 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
en->lsa.age = LSA_MAXAGE;
en->lsa.sn = LSA_MAXSEQNO;
lsasum_calculate(&en->lsa, sum);
-
- OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)",
- en->lsa.id, en->lsa.type);
ospf_lsupd_flood(po, NULL, NULL, &en->lsa, oa->areaid, 1);
if (can_flush_lsa(po)) flush_lsa(en, po);
}
@@ -1131,15 +1131,11 @@ originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src,
}
void
-flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn)
+flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa)
{
struct proto_ospf *po = oa->po;
struct proto *p = &po->proto;
struct top_hash_entry *en;
- int nssa = oa_is_nssa(oa);
-
- OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d",
- nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
u32 dom = nssa ? oa->areaid : 0;
u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT;
@@ -1147,6 +1143,9 @@ flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn)
if (en = ospf_hash_find(po->gr, dom, lsaid, po->router_id, type))
{
+ OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d",
+ nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
+
if (check_ext_lsa(en, fn, 0, IPA_NONE, 0) < 0)
{
log(L_ERR "%s: LSAID collision for %I/%d",
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index b9bc9cf6..cb876487 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -72,7 +72,7 @@ void originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric
void originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED);
void flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type);
void originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, u32 metric, ip_addr fwaddr, u32 tag, int pbit);
-void flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn);
+void flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa);
#ifdef OSPFv2
diff --git a/proto/radv/config.Y b/proto/radv/config.Y
index 495d9a05..abccd2c7 100644
--- a/proto/radv/config.Y
+++ b/proto/radv/config.Y
@@ -14,32 +14,45 @@ CF_DEFINES
#define RADV_CFG ((struct radv_config *) this_proto)
#define RADV_IFACE ((struct radv_iface_config *) this_ipatt)
#define RADV_PREFIX this_radv_prefix
+#define RADV_RDNSS (&this_radv_rdnss)
+#define RADV_DNSSL (&this_radv_dnssl)
static struct radv_prefix_config *this_radv_prefix;
+static struct radv_rdnss_config this_radv_rdnss;
+static struct radv_dnssl_config this_radv_dnssl;
+static list radv_dns_list; /* Used by radv_rdnss and radv_dnssl */
+static u8 radv_mult_val; /* Used by radv_mult for second return value */
+
CF_DECLS
CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS,
- TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED,
- LIFETIME, SKIP, ONLINK, AUTONOMOUS)
+ TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
+ LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN,
+ LOCAL)
+%type<i> radv_mult
CF_GRAMMAR
-CF_ADDTO(proto, radv_proto '}')
+CF_ADDTO(proto, radv_proto)
radv_proto_start: proto_start RADV
{
this_proto = proto_config_new(&proto_radv, sizeof(struct radv_config), $1);
init_list(&RADV_CFG->patt_list);
init_list(&RADV_CFG->pref_list);
+ init_list(&RADV_CFG->rdnss_list);
+ init_list(&RADV_CFG->dnssl_list);
};
radv_proto_item:
proto_item
- | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
| INTERFACE radv_iface
+ | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
+ | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); }
+ | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); }
;
radv_proto_opts:
@@ -48,7 +61,7 @@ radv_proto_opts:
;
radv_proto:
- radv_proto_start proto_name '{' radv_proto_opts;
+ radv_proto_start proto_name '{' radv_proto_opts '}';
radv_iface_start:
@@ -57,6 +70,8 @@ radv_iface_start:
add_tail(&RADV_CFG->patt_list, NODE this_ipatt);
init_list(&this_ipatt->ipn_list);
init_list(&RADV_IFACE->pref_list);
+ init_list(&RADV_IFACE->rdnss_list);
+ init_list(&RADV_IFACE->dnssl_list);
RADV_IFACE->min_ra_int = -1; /* undefined */
RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT;
@@ -77,6 +92,10 @@ radv_iface_item:
| CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255)) cf_error("Current hop limit must be in range 0-255"); }
| DEFAULT LIFETIME expr { RADV_IFACE->default_lifetime = $3; if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); }
| PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); }
+ | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); }
+ | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); }
+ | RDNSS LOCAL bool { RADV_IFACE->rdnss_local = $3; }
+ | DNSSL LOCAL bool { RADV_IFACE->dnssl_local = $3; }
;
radv_iface_finish:
@@ -152,6 +171,103 @@ radv_prefix:
radv_prefix_start radv_prefix_opt_list radv_prefix_finish;
+
+radv_rdnss_node: ipa
+{
+ struct radv_rdnss_config *cf = cfg_allocz(sizeof(struct radv_rdnss_config));
+ add_tail(&radv_dns_list, NODE cf);
+
+ cf->server = $1;
+ cf->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
+};
+
+radv_rdnss_start:
+{
+ RADV_RDNSS->lifetime = 0;
+ RADV_RDNSS->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
+};
+
+radv_rdnss_item:
+ | NS radv_rdnss_node
+ | LIFETIME radv_mult { RADV_RDNSS->lifetime = $2; RADV_RDNSS->lifetime_mult = radv_mult_val; }
+ ;
+
+radv_rdnss_finish:
+{
+ if (EMPTY_LIST(radv_dns_list))
+ cf_error("No nameserver in RDNSS section");
+
+ struct radv_rdnss_config *cf;
+ WALK_LIST(cf, radv_dns_list)
+ {
+ cf->lifetime = RADV_RDNSS->lifetime;
+ cf->lifetime_mult = RADV_RDNSS->lifetime_mult;
+ }
+};
+
+radv_rdnss_opts:
+ /* empty */
+ | radv_rdnss_opts radv_rdnss_item ';'
+ ;
+
+radv_rdnss:
+ radv_rdnss_node
+ | '{' radv_rdnss_start radv_rdnss_opts '}' radv_rdnss_finish
+ ;
+
+
+radv_dnssl_node: TEXT
+{
+ struct radv_dnssl_config *cf = cfg_allocz(sizeof(struct radv_dnssl_config));
+ add_tail(&radv_dns_list, NODE cf);
+
+ cf->domain = $1;
+ cf->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
+
+ if (radv_process_domain(cf) < 0)
+ cf_error("Invalid domain dame");
+};
+
+radv_dnssl_start:
+{
+ RADV_DNSSL->lifetime = 0;
+ RADV_DNSSL->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
+};
+
+radv_dnssl_item:
+ | DOMAIN radv_dnssl_node
+ | LIFETIME radv_mult { RADV_DNSSL->lifetime = $2; RADV_DNSSL->lifetime_mult = radv_mult_val; }
+ ;
+
+radv_dnssl_finish:
+{
+ if (EMPTY_LIST(radv_dns_list))
+ cf_error("No domain in DNSSL section");
+
+ struct radv_dnssl_config *cf;
+ WALK_LIST(cf, radv_dns_list)
+ {
+ cf->lifetime = RADV_DNSSL->lifetime;
+ cf->lifetime_mult = RADV_DNSSL->lifetime_mult;
+ }
+};
+
+radv_dnssl_opts:
+ /* empty */
+ | radv_dnssl_opts radv_dnssl_item ';'
+ ;
+
+radv_dnssl:
+ radv_dnssl_node
+ | '{' radv_dnssl_start radv_dnssl_opts '}' radv_dnssl_finish
+ ;
+
+
+radv_mult:
+ expr { $$ = $1; radv_mult_val = 0; }
+ | MULT expr { $$ = 0; radv_mult_val = $2; if (($2 < 1) || ($2 > 254)) cf_error("Multiplier must be in range 1-254"); }
+ ;
+
CF_CODE
CF_END
diff --git a/proto/radv/packets.c b/proto/radv/packets.c
index ac59ce94..6fdfcaa3 100644
--- a/proto/radv/packets.c
+++ b/proto/radv/packets.c
@@ -24,8 +24,10 @@ struct radv_ra_packet
#define OPT_RA_MANAGED 0x80
#define OPT_RA_OTHER_CFG 0x40
-#define OPT_PREFIX 3
-#define OPT_MTU 5
+#define OPT_PREFIX 3
+#define OPT_MTU 5
+#define OPT_RDNSS 25
+#define OPT_DNSSL 31
struct radv_opt_prefix
{
@@ -50,6 +52,25 @@ struct radv_opt_mtu
u32 mtu;
};
+struct radv_opt_rdnss
+{
+ u8 type;
+ u8 length;
+ u16 reserved;
+ u32 lifetime;
+ ip_addr servers[];
+};
+
+struct radv_opt_dnssl
+{
+ u8 type;
+ u8 length;
+ u16 reserved;
+ u32 lifetime;
+ char domain[];
+};
+
+
static struct radv_prefix_config default_prefix = {
.onlink = 1,
.autonomous = 1,
@@ -57,6 +78,7 @@ static struct radv_prefix_config default_prefix = {
.preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
};
+
static struct radv_prefix_config *
radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
{
@@ -78,10 +100,146 @@ radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
return &default_prefix;
}
+static int
+radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend)
+{
+ struct radv_rdnss_config *rcf = HEAD(*rdnss_list);
+
+ while(NODE_VALID(rcf))
+ {
+ struct radv_rdnss_config *rcf_base = rcf;
+ struct radv_opt_rdnss *op = (void *) *buf;
+ int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip_addr);
+ int i = 0;
+
+ if (max_i < 1)
+ goto too_much;
+
+ op->type = OPT_RDNSS;
+ op->reserved = 0;
+
+ if (rcf->lifetime_mult)
+ op->lifetime = htonl(rcf->lifetime_mult * ifa->cf->max_ra_int);
+ else
+ op->lifetime = htonl(rcf->lifetime);
+
+ while(NODE_VALID(rcf) &&
+ (rcf->lifetime == rcf_base->lifetime) &&
+ (rcf->lifetime_mult == rcf_base->lifetime_mult))
+ {
+ if (i >= max_i)
+ goto too_much;
+
+ op->servers[i] = rcf->server;
+ ipa_hton(op->servers[i]);
+ i++;
+
+ rcf = NODE_NEXT(rcf);
+ }
+
+ op->length = 1+2*i;
+ *buf += 8 * op->length;
+ }
+
+ return 0;
+
+ too_much:
+ log(L_WARN "%s: Too many RA options on interface %s",
+ ifa->ra->p.name, ifa->iface->name);
+ return -1;
+}
+
+int
+radv_process_domain(struct radv_dnssl_config *cf)
+{
+ /* Format of domain in search list is <size> <label> <size> <label> ... 0 */
+
+ char *dom = cf->domain;
+ char *dom_end = dom; /* Just to */
+ u8 *dlen_save = &cf->dlen_first;
+ int len;
+
+ while (dom_end)
+ {
+ dom_end = strchr(dom, '.');
+ len = dom_end ? (dom_end - dom) : strlen(dom);
+
+ if (len < 1 || len > 63)
+ return -1;
+
+ *dlen_save = len;
+ dlen_save = (u8 *) dom_end;
+
+ dom += len + 1;
+ }
+
+ len = dom - cf->domain;
+ if (len > 254)
+ return -1;
+
+ cf->dlen_all = len;
+
+ return 0;
+}
+
+static int
+radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *bufend)
+{
+ struct radv_dnssl_config *dcf = HEAD(*dnssl_list);
+
+ while(NODE_VALID(dcf))
+ {
+ struct radv_dnssl_config *dcf_base = dcf;
+ struct radv_opt_dnssl *op = (void *) *buf;
+ int bsize = bufend - *buf - sizeof(struct radv_opt_dnssl);
+ int bpos = 0;
+
+ if (bsize < 0)
+ goto too_much;
+
+ bsize = bsize & ~7; /* Round down to multiples of 8 */
+
+ op->type = OPT_DNSSL;
+ op->reserved = 0;
+
+ if (dcf->lifetime_mult)
+ op->lifetime = htonl(dcf->lifetime_mult * ifa->cf->max_ra_int);
+ else
+ op->lifetime = htonl(dcf->lifetime);
+
+ while(NODE_VALID(dcf) &&
+ (dcf->lifetime == dcf_base->lifetime) &&
+ (dcf->lifetime_mult == dcf_base->lifetime_mult))
+ {
+ if (bpos + dcf->dlen_all + 1 > bsize)
+ goto too_much;
+
+ op->domain[bpos++] = dcf->dlen_first;
+ memcpy(op->domain + bpos, dcf->domain, dcf->dlen_all);
+ bpos += dcf->dlen_all;
+
+ dcf = NODE_NEXT(dcf);
+ }
+
+ int blen = (bpos + 7) / 8;
+ bzero(op->domain + bpos, 8 * blen - bpos);
+ op->length = 1 + blen;
+ *buf += 8 * op->length;
+ }
+
+ return 0;
+
+ too_much:
+ log(L_WARN "%s: Too many RA options on interface %s",
+ ifa->ra->p.name, ifa->iface->name);
+ return -1;
+}
+
static void
radv_prepare_ra(struct radv_iface *ifa)
{
struct proto_radv *ra = ifa->ra;
+ struct radv_config *cf = (struct radv_config *) (ra->p.cf);
char *buf = ifa->sk->tbuf;
char *bufstart = buf;
@@ -121,7 +279,7 @@ radv_prepare_ra(struct radv_iface *ifa)
if (buf + sizeof(struct radv_opt_prefix) > bufend)
{
log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name);
- break;
+ goto done;
}
struct radv_opt_prefix *op = (void *) buf;
@@ -137,7 +295,22 @@ radv_prepare_ra(struct radv_iface *ifa)
ipa_hton(op->prefix);
buf += sizeof(*op);
}
-
+
+ if (! ifa->cf->rdnss_local)
+ if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0)
+ goto done;
+
+ if (radv_prepare_rdnss(ifa, &ifa->cf->rdnss_list, &buf, bufend) < 0)
+ goto done;
+
+ if (! ifa->cf->dnssl_local)
+ if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0)
+ goto done;
+
+ if (radv_prepare_dnssl(ifa, &ifa->cf->dnssl_list, &buf, bufend) < 0)
+ goto done;
+
+ done:
ifa->plen = buf - bufstart;
}
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index d6fc8da5..5e7296a3 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -29,6 +29,10 @@
* radv_iface_notify(), which processes asynchronous events (specified
* by RA_EV_* codes), and radv_timer(), which triggers sending RAs and
* computes the next timeout.
+ *
+ * Supported standards:
+ * - RFC 4861 - main RA standard
+ * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
*/
static void
diff --git a/proto/radv/radv.h b/proto/radv/radv.h
index 12bfe42e..48af8c00 100644
--- a/proto/radv/radv.h
+++ b/proto/radv/radv.h
@@ -42,24 +42,33 @@
#define DEFAULT_VALID_LIFETIME 86400
#define DEFAULT_PREFERRED_LIFETIME 14400
+#define DEFAULT_DNS_LIFETIME_MULT 3
+
struct radv_config
{
struct proto_config c;
list patt_list; /* List of iface configs (struct radv_iface_config) */
list pref_list; /* Global list of prefix configs (struct radv_prefix_config) */
+ list rdnss_list; /* Global list of RDNSS configs (struct radv_rdnss_config) */
+ list dnssl_list; /* Global list of DNSSL configs (struct radv_dnssl_config) */
};
struct radv_iface_config
{
struct iface_patt i;
list pref_list; /* Local list of prefix configs (struct radv_prefix_config) */
+ list rdnss_list; /* Local list of RDNSS configs (struct radv_rdnss_config) */
+ list dnssl_list; /* Local list of DNSSL configs (struct radv_dnssl_config) */
u32 min_ra_int; /* Standard options from RFC 4261 */
u32 max_ra_int;
u32 min_delay;
- u8 managed;
+ u8 rdnss_local; /* Global list is not used for RDNSS */
+ u8 dnssl_local; /* Global list is not used for DNSSL */
+
+ u8 managed; /* Standard options from RFC 4261 */
u8 other_config;
u32 link_mtu;
u32 reachable_time;
@@ -81,6 +90,25 @@ struct radv_prefix_config
u32 preferred_lifetime;
};
+struct radv_rdnss_config
+{
+ node n;
+ u32 lifetime; /* Valid if lifetime_mult is 0 */
+ u16 lifetime_mult; /* Lifetime specified as multiple of max_ra_int */
+ ip_addr server; /* IP address of recursive DNS server */
+};
+
+struct radv_dnssl_config
+{
+ node n;
+ u32 lifetime; /* Valid if lifetime_mult is 0 */
+ u16 lifetime_mult; /* Lifetime specified as multiple of max_ra_int */
+ u8 dlen_first; /* Length of first label in domain */
+ u8 dlen_all; /* Both dlen_ filled in radv_process_domain() */
+ char *domain; /* Domain for DNS search list, in processed form */
+};
+
+
struct proto_radv
{
struct proto p;
@@ -123,6 +151,7 @@ struct radv_iface
void radv_iface_notify(struct radv_iface *ifa, int event);
/* packets.c */
+int radv_process_domain(struct radv_dnssl_config *cf);
void radv_send_ra(struct radv_iface *ifa, int shutdown);
int radv_sk_open(struct radv_iface *ifa);