summaryrefslogtreecommitdiff
path: root/proto/radv/packets.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/radv/packets.c')
-rw-r--r--proto/radv/packets.c181
1 files changed, 177 insertions, 4 deletions
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;
}