changeset 2566:4e83d8f9ee4f draft

Fix defining new options in dhcpcd.conf and requesting them.
author Roy Marples <roy@marples.name>
date Wed, 02 Jul 2014 18:59:19 +0000
parents 4fcda038fb87
children 91bec8cafd6d
files dhcp-common.c dhcp-common.h dhcp.c dhcp6.c if-options.c
diffstat 5 files changed, 128 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/dhcp-common.c	Wed Jul 02 13:24:55 2014 +0000
+++ b/dhcp-common.c	Wed Jul 02 18:59:19 2014 +0000
@@ -86,6 +86,7 @@
 
 int
 make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
+    const struct dhcp_opt *odopts, size_t odopts_len,
     uint8_t *mask, const char *opts, int add)
 {
 	char *token, *o, *p, *t;
@@ -94,14 +95,14 @@
 	unsigned int n;
 	size_t i;
 
-	o = p = strdup(opts);
 	if (opts == NULL)
 		return -1;
+	o = p = strdup(opts);
 	while ((token = strsep(&p, ", "))) {
 		if (*token == '\0')
 			continue;
-		for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
-			match = 0;
+		match = 0;
+		for (i = 0, opt = odopts; i < odopts_len; i++, opt++) {
 			if (strcmp(opt->var, token) == 0)
 				match = 1;
 			else {
@@ -111,26 +112,40 @@
 					if (opt->option == n)
 						match = 1;
 			}
-			if (match) {
-				if (add == 2 && !(opt->type & ADDRIPV4)) {
-					free(o);
-					errno = EINVAL;
-					return -1;
+			if (match)
+				break;
+		}
+		if (match == 0) {
+			for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
+				if (strcmp(opt->var, token) == 0)
+				        match = 1;
+				else {
+					errno = 0;
+					n = (unsigned int)strtol(token, &t, 0);
+					if (errno == 0 && !*t)
+						if (opt->option == n)
+							match = 1;
 				}
-				if (add == 1 || add == 2)
-					add_option_mask(mask,
-					    opt->option);
-				else
-					del_option_mask(mask,
-					    opt->option);
-				break;
+				if (match)
+					break;
 			}
 		}
-		if (!opt->option) {
+		if (!match || !opt->option) {
 			free(o);
 			errno = ENOENT;
 			return -1;
 		}
+		if (add == 2 && !(opt->type & ADDRIPV4)) {
+			free(o);
+			errno = EINVAL;
+			return -1;
+		}
+		if (add == 1 || add == 2)
+			add_option_mask(mask,
+			    opt->option);
+		else
+			del_option_mask(mask,
+			    opt->option);
 	}
 	free(o);
 	return 0;
--- a/dhcp-common.h	Wed Jul 02 13:24:55 2014 +0000
+++ b/dhcp-common.h	Wed Jul 02 18:59:19 2014 +0000
@@ -88,6 +88,7 @@
 #define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
 #define has_option_mask(var, val) (var[val >>3] & (1 << (val & 7)))
 int make_option_mask(const struct dhcp_opt *, size_t,
+    const struct dhcp_opt *, size_t,
     uint8_t *, const char *, int);
 
 size_t encode_rfc1035(const char *src, uint8_t *dst);
--- a/dhcp.c	Wed Jul 02 13:24:55 2014 +0000
+++ b/dhcp.c	Wed Jul 02 18:59:19 2014 +0000
@@ -943,6 +943,30 @@
 				goto toobig;
 			*p++ = (uint8_t)opt->option;
 		}
+		for (i = 0, opt = ifo->dhcp_override;
+		    i < ifo->dhcp_override_len;
+		    i++, opt++)
+		{
+			/* Check if added above */
+			for (lp = n_params + 1; lp < p; lp++)
+				if (*lp == (uint8_t)opt->option)
+					break;
+			if (lp < p)
+				continue;
+			if (!(opt->type & REQUEST ||
+				has_option_mask(ifo->requestmask, opt->option)))
+				continue;
+			if (opt->type & NOREQ)
+				continue;
+			if (type == DHCP_INFORM &&
+			    (opt->option == DHO_RENEWALTIME ||
+				opt->option == DHO_REBINDTIME))
+				continue;
+			len = (size_t)((p - m) + 2);
+			if (len > sizeof(*dhcp))
+				goto toobig;
+			*p++ = (uint8_t)opt->option;
+		}
 		*n_params = (uint8_t)(p - n_params - 1);
 	}
 
--- a/dhcp6.c	Wed Jul 02 13:24:55 2014 +0000
+++ b/dhcp6.c	Wed Jul 02 18:59:19 2014 +0000
@@ -311,11 +311,11 @@
 	struct dhcp6_message *m;
 	struct dhcp6_option *o, *so;
 	const struct dhcp6_option *si, *unicast;
-	size_t l, len, ml, auth_len;
+	size_t l, n, len, ml, auth_len;
 	uint8_t u8, type;
 	uint16_t *u16, n_options;
 	struct if_options *ifo;
-	const struct dhcp_opt *opt;
+	const struct dhcp_opt *opt, *opt2;
 	uint8_t IA, *p;
 	uint32_t u32;
 	const struct ipv6_addr *ap;
@@ -356,6 +356,27 @@
 		    l < ifp->ctx->dhcp6_opts_len;
 		    l++, opt++)
 		{
+			for (n = 0, opt2 = ifo->dhcp6_override;
+			    n < ifo->dhcp6_override_len;
+			    n++, opt2++)
+			{
+				if (opt->option == opt2->option)
+					break;
+			}
+			if (n < ifo->dhcp6_override_len)
+			    continue;
+			if (!(opt->type & NOREQ) &&
+			    (opt->type & REQUEST ||
+			    has_option_mask(ifo->requestmask6, opt->option)))
+			{
+				n_options++;
+				len += sizeof(*u16);
+			}
+		}
+		for (l = 0, opt = ifo->dhcp6_override;
+		    l < ifo->dhcp6_override_len;
+		    l++, opt++)
+		{
 			if (!(opt->type & NOREQ) &&
 			    (opt->type & REQUEST ||
 			    has_option_mask(ifo->requestmask6, opt->option)))
@@ -628,6 +649,28 @@
 			    l < ifp->ctx->dhcp6_opts_len;
 			    l++, opt++)
 			{
+				for (n = 0, opt2 = ifo->dhcp6_override;
+				    n < ifo->dhcp6_override_len;
+				    n++, opt2++)
+				{
+					if (opt->option == opt2->option)
+						break;
+				}
+				if (n < ifo->dhcp6_override_len)
+				    continue;
+				if (!(opt->type & NOREQ) &&
+				    (opt->type & REQUEST ||
+				    has_option_mask(ifo->requestmask6,
+				        opt->option)))
+				{
+					*u16++ = htons(opt->option);
+					o->len += sizeof(*u16);
+				}
+			}
+			for (l = 0, opt = ifo->dhcp6_override;
+			    l < ifo->dhcp6_override_len;
+			    l++, opt++)
+			{
 				if (!(opt->type & NOREQ) &&
 				    (opt->type & REQUEST ||
 				    has_option_mask(ifo->requestmask6,
--- a/if-options.c	Wed Jul 02 13:24:55 2014 +0000
+++ b/if-options.c	Wed Jul 02 18:59:19 2014 +0000
@@ -518,7 +518,9 @@
 
 static const char *
 set_option_space(struct dhcpcd_ctx *ctx,
-    const char *arg, const struct dhcp_opt **d, size_t *dl,
+    const char *arg,
+    const struct dhcp_opt **d, size_t *dl,
+    const struct dhcp_opt **od, size_t *odl,
     struct if_options *ifo,
     uint8_t *request[], uint8_t *require[], uint8_t *no[])
 {
@@ -527,6 +529,8 @@
 	if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
 		*d = ctx->dhcp6_opts;
 		*dl = ctx->dhcp6_opts_len;
+		*od = ifo->dhcp6_override;
+		*odl = ifo->dhcp6_override_len;
 		*request = ifo->requestmask6;
 		*require = ifo->requiremask6;
 		*no = ifo->nomask6;
@@ -537,9 +541,13 @@
 #ifdef INET
 	*d = ctx->dhcp_opts;
 	*dl = ctx->dhcp_opts_len;
+	*od = ifo->dhcp_override;
+	*odl = ifo->dhcp_override_len;
 #else
 	*d = NULL;
 	*dl = 0;
+	*od = NULL;
+	*odl = 0;
 #endif
 	*request = ifo->requestmask;
 	*require = ifo->requiremask;
@@ -630,10 +638,10 @@
 	struct in_addr addr, addr2;
 	in_addr_t *naddr;
 	struct rt *rt;
-	const struct dhcp_opt *d;
+	const struct dhcp_opt *d, *od;
 	uint8_t *request, *require, *no;
 	struct dhcp_opt **dop, *ndop;
-	size_t *dop_len, dl;
+	size_t *dop_len, dl, odl;
 	struct vivco *vivco;
 	struct token *token;
 	struct group *grp;
@@ -733,10 +741,10 @@
 		}
 		break;
 	case 'o':
-		arg = set_option_space(ctx, arg, &d, &dl, ifo,
+		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
 		    &request, &require, &no);
-		if (make_option_mask(d, dl, request, arg, 1) != 0 ||
-		    make_option_mask(d, dl, no, arg, -1) != 0)
+		if (make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||
+		    make_option_mask(d, dl, od, odl, no, arg, -1) != 0)
 		{
 			syslog(LOG_ERR, "unknown option `%s'", arg);
 			return -1;
@@ -952,22 +960,22 @@
 		ifo->options |= DHCPCD_MASTER;
 		break;
 	case 'O':
-		arg = set_option_space(ctx, arg, &d, &dl, ifo,
+		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
 		    &request, &require, &no);
-		if (make_option_mask(d, dl, request, arg, -1) != 0 ||
-		    make_option_mask(d, dl, require, arg, -1) != 0 ||
-		    make_option_mask(d, dl, no, arg, 1) != 0)
+		if (make_option_mask(d, dl, od, odl, request, arg, -1) != 0 ||
+		    make_option_mask(d, dl, od, odl, require, arg, -1) != 0 ||
+		    make_option_mask(d, dl, od, odl, no, arg, 1) != 0)
 		{
 			syslog(LOG_ERR, "unknown option `%s'", arg);
 			return -1;
 		}
 		break;
 	case 'Q':
-		arg = set_option_space(ctx, arg, &d, &dl, ifo,
+		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
 		    &request, &require, &no);
-		if (make_option_mask(d, dl, require, arg, 1) != 0 ||
-		    make_option_mask(d, dl, request, arg, 1) != 0 ||
-		    make_option_mask(d, dl, no, arg, -1) != 0)
+		if (make_option_mask(d, dl, od, odl, require, arg, 1) != 0 ||
+		    make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||
+		    make_option_mask(d, dl, od, odl, no, arg, -1) != 0)
 		{
 			syslog(LOG_ERR, "unknown option `%s'", arg);
 			return -1;
@@ -1156,8 +1164,11 @@
 		}
 		break;
 	case O_DESTINATION:
-		if (make_option_mask(ctx->dhcp_opts, ctx->dhcp_opts_len,
-		    ifo->dstmask, arg, 2) != 0) {
+		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
+		    &request, &require, &no);
+		if (make_option_mask(d, dl, od, odl,
+		    ifo->dstmask, arg, 2) != 0)
+		{
 			if (errno == EINVAL)
 				syslog(LOG_ERR, "option `%s' does not take"
 				    " an IPv4 address", arg);