/* * BIRD -- RAdv Packet Processing * * * 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; ip_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; ip_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; struct radv_config *cf = (void *) p->p.cf; u8 px_blocks = (rt->n.pxlen + 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; } struct radv_opt_route *opt = (void *) *buf; *buf += opt_len; opt->type = OPT_ROUTE; opt->length = 1 + px_blocks; opt->pxlen = rt->n.pxlen; opt->flags = rt->preference; if (p->valid && (p->active || !cf->route_lifetime_sensitive) && rt->alive) opt->lifetime = htonl(rt->lifetime_set ? rt->lifetime : cf->route_lifetime); else opt->lifetime = 0; /* Copy the relevant part of the prefix */ ip6_addr px_addr = ip6_hton(rt->n.prefix); memcpy(opt->prefix, &px_addr, 8 * px_blocks); 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(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