diff --git a/proto/radv/config.Y b/proto/radv/config.Y index 8d4a3ab9..5c213d50 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -25,6 +25,15 @@ 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 */ +static inline void +radv_add_to_custom_list(list *l, int type, struct bytestring *payload) +{ + if (type < 0 || type > 255) cf_error("RA cusom type must be in range 0-255"); + struct radv_custom_config *cf = cfg_allocz(sizeof(struct radv_custom_config)); + add_tail(l, NODE cf); + cf->type = type; + cf->payload = payload; +} CF_DECLS @@ -52,6 +61,7 @@ radv_proto_start: proto_start RADV init_list(&RADV_CFG->pref_list); init_list(&RADV_CFG->rdnss_list); init_list(&RADV_CFG->dnssl_list); + init_list(&RADV_CFG->custom_list); }; radv_proto_item: @@ -61,6 +71,7 @@ radv_proto_item: | 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); } + | OTHER TYPE expr BYTESTRING { radv_add_to_custom_list(&RADV_CFG->custom_list, $3, $4); } | TRIGGER net_ip6 { RADV_CFG->trigger = $2; } | PROPAGATE ROUTES bool { RADV_CFG->propagate_routes = $3; } ; @@ -82,6 +93,7 @@ radv_iface_start: init_list(&RADV_IFACE->pref_list); init_list(&RADV_IFACE->rdnss_list); init_list(&RADV_IFACE->dnssl_list); + init_list(&RADV_IFACE->custom_list); RADV_IFACE->min_ra_int = (u32) -1; /* undefined */ RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT; @@ -124,8 +136,10 @@ radv_iface_item: | 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); } + | OTHER TYPE expr BYTESTRING { radv_add_to_custom_list(&RADV_IFACE->custom_list, $3, $4); } | RDNSS LOCAL bool { RADV_IFACE->rdnss_local = $3; } | DNSSL LOCAL bool { RADV_IFACE->dnssl_local = $3; } + | OTHER LOCAL bool { RADV_IFACE->custom_local = $3; } ; radv_preference: diff --git a/proto/radv/packets.c b/proto/radv/packets.c index 5cd8b2de..d1f86ec1 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -82,6 +82,13 @@ struct radv_opt_dnssl char domain[]; }; +struct radv_opt_custom +{ + u8 type; + u8 length; + u8 payload[]; +}; + static int radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt, char **buf, char *bufend) @@ -254,6 +261,36 @@ radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *b return -1; } +static int +radv_prepare_custom(struct radv_iface *ifa, list *custom_list, char **buf, char *bufend) +{ + struct radv_custom_config *ccf = HEAD(*custom_list); + + while(NODE_VALID(ccf)) + { + struct radv_opt_custom *op = (void *) *buf; + /* Add 2 octets for type and size and 8 - 1 for ceiling the division up to 8 octets */ + int size = (ccf->payload->length + 2 + 8 - 1) / 8; + if (bufend - *buf < size * 8) + goto too_much; + + memset(op, 0, size * 8); /* Clear buffer so there is no tail garbage */ + op->type = ccf->type; + op->length = size; + memcpy(op->payload, ccf->payload->data, ccf->payload->length); + + *buf += 8 * op->length; + ccf = NODE_NEXT(ccf); + } + + 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 int radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *px, char **buf, char *bufend) @@ -352,6 +389,14 @@ radv_prepare_ra(struct radv_iface *ifa) if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) goto done; + if (! ic->custom_local) + if (radv_prepare_custom(ifa, &cf->custom_list, &buf, bufend) < 0) + goto done; + + if (radv_prepare_custom(ifa, &ic->custom_list, &buf, bufend) < 0) + goto done; + + if (p->fib_up) { FIB_WALK(&p->routes, struct radv_route, rt) diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 14d40f8a..8c716158 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -51,6 +51,7 @@ struct radv_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) */ + list custom_list; /* Global list of custom configs (struct radv_custom_config) */ net_addr trigger; /* Prefix of a trigger route, if defined */ u8 propagate_routes; /* Do we propagate more specific routes (RFC 4191)? */ @@ -63,6 +64,7 @@ struct radv_iface_config 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) */ + list custom_list; /* Local list of custom configs (struct radv_custom_config) */ u32 min_ra_int; /* Standard options from RFC 4861 */ u32 max_ra_int; @@ -75,6 +77,7 @@ struct radv_iface_config u8 rdnss_local; /* Global list is not used for RDNSS */ u8 dnssl_local; /* Global list is not used for DNSSL */ + u8 custom_local; /* Global list is not used for custom */ u8 managed; /* Standard options from RFC 4861 */ u8 other_config; @@ -122,6 +125,13 @@ struct radv_dnssl_config const char *domain; /* Domain for DNS search list, in processed form */ }; +struct radv_custom_config +{ + node n; + u8 type; /* Identifier of the type of option */ + struct bytestring *payload; /* Payload of the option */ +}; + /* * One more specific route as per RFC 4191. *