/* * BIRD -- RAdv Packet Processing * * (c) 2011--2019 Ondrej Zajicek * (c) 2011--2019 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include "radv.h" struct radv_ra_packet { u8 type; u8 code; u16 checksum; u8 current_hop_limit; u8 flags; u16 router_lifetime; u32 reachable_time; u32 retrans_timer; }; #define OPT_RA_MANAGED 0x80 #define OPT_RA_OTHER_CFG 0x40 #define OPT_PREFIX 3 #define OPT_MTU 5 #define OPT_ROUTE 24 #define OPT_RDNSS 25 #define OPT_DNSSL 31 struct radv_opt_prefix { u8 type; u8 length; u8 pxlen; u8 flags; u32 valid_lifetime; u32 preferred_lifetime; u32 reserved; ip6_addr prefix; }; #define OPT_PX_ONLINK 0x80 #define OPT_PX_AUTONOMOUS 0x40 struct radv_opt_mtu { u8 type; u8 length; u16 reserved; u32 mtu; }; struct radv_opt_route { u8 type; u8 length; u8 pxlen; u8 flags; u32 lifetime; u8 prefix[]; }; struct radv_opt_rdnss { u8 type; u8 length; u16 reserved; u32 lifetime; ip6_addr servers[]; }; struct radv_opt_dnssl { u8 type; u8 length; u16 reserved; u32 lifetime; char domain[]; }; static int radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt, char **buf, char *bufend) { struct radv_proto *p = ifa->ra; u8 px_blocks = (net6_pxlen(rt->n.addr) + 63) / 64; u8 opt_len = 8 * (1 + px_blocks); if (*buf + opt_len > bufend) { log(L_WARN, "%s: Too many RA options on interface %s", p->p.name, ifa->iface->name); return -1; } uint preference = rt->preference_set ? rt->preference : ifa->cf->route_preference; uint lifetime = rt->lifetime_set ? rt->lifetime : ifa->cf->route_lifetime; uint valid = rt->valid && p->valid && (p->active || !ifa->cf->route_lifetime_sensitive); struct radv_opt_route *opt = (void *) *buf; *buf += opt_len; opt->type = OPT_ROUTE; opt->length = 1 + px_blocks; opt->pxlen = net6_pxlen(rt->n.addr); opt->flags = preference; opt->lifetime = valid ? htonl(lifetime) : 0; /* Copy the relevant part of the prefix */ ip6_addr px_addr = ip6_hton(net6_prefix(rt->n.addr)); memcpy(opt->prefix, &px_addr, 8 * px_blocks); /* Keeping track of first linger timeout */ if (!rt->valid) ifa->valid_time = MIN(ifa->valid_time, rt->changed + ifa->cf->route_linger_time S); return 0; } 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(ip6_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] = ip6_hton(rcf->server); 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