diff --git a/cfparse.y b/cfparse.y index c374566..2dac926 100644 --- a/cfparse.y +++ b/cfparse.y @@ -41,6 +41,10 @@ #include #include +/* XXX */ +#include +#include + #include "dhcp6.h" #include "config.h" #include "common.h" @@ -157,6 +161,9 @@ void cf_init(void); %type rangeparam %type poolparam +/* XXX */ +%token RAW + %% statements: /* empty */ @@ -676,6 +685,64 @@ dhcpoption: /* currently no value */ $$ = l; } + /* XXX */ + | RAW NUMBER STRING + { + struct cf_list *l; + struct rawoption *rawop; + char *tmp, *opstr = $2, *datastr = $3; + + yywarn("Got raw option: %s %s", opstr, datastr); + + if ((rawop = malloc(sizeof(*rawop))) == NULL) { + yywarn("can't allocate memory"); + free(datastr); + free(opstr); + return (-1); + } + + /* convert op num */ + rawop->opnum = (int)strtol(opstr, NULL, 10); + + /* convert string to lowercase */ + tmp = datastr; + for ( ; *tmp; ++tmp) *tmp = tolower(*tmp); + + /* allocate buffer */ + int len = strlen(datastr); + len -= len / 3; /* remove ':' from length */ + len = len / 2; /* byte length */ + rawop->datalen = len; + + if ((rawop->data = malloc(len)) == NULL) { + yywarn("can't allocate memory"); + free(datastr); + free(opstr); + return (-1); + } + + /* convert hex string to byte array */ + char *h = datastr; + char *b = rawop->data; + char xlate[] = "0123456789abcdef"; + int p1, p2, i = 0; + + for ( ; *h; h += 3, ++b) { /* string is xx(:xx)\0 */ + p1 = (int)(strchr(xlate, *h) - xlate); + p2 = (int)(strchr(xlate, *(h+1)) - xlate); + *b = (char)((p1 * 16) + p2); + } + //free(datastr); + //free(opstr); + + yywarn("Raw option %d length %d stored at %p with data at %p", + rawop->opnum, rawop->datalen, (void*)rawop, (void*)rawop->data); + + MAKE_CFLIST(l, DHCPOPT_RAW, NULL, NULL); + l->ptr = rawop; + $$ = l; + } + | DNS_SERVERS { struct cf_list *l; diff --git a/cftoken.l b/cftoken.l index a9ed5c8..890b306 100644 --- a/cftoken.l +++ b/cftoken.l @@ -117,6 +117,9 @@ slash \/ bcl \{ ecl \} +/* XXX */ +hexdata {hexpair}(:{hexpair})* + %s S_CNF %s S_IFACE %s S_PREF @@ -128,6 +131,8 @@ ecl \} %s S_SECRET %s S_ADDRPOOL %s S_INCL +/* XXX */ +%s S_RAW %% %{ @@ -214,6 +219,16 @@ ecl \} bcmcs-server-domain-name { DECHO; return (BCMCS_NAME); } refreshtime { DECHO; return (REFRESHTIME); } + /* XXX */ +raw-option { DECHO; BEGIN S_RAW; return (RAW); } +{integer} { DECHO; yylval.str = strdup(yytext); return(NUMBER); } +{hexdata} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (STRING); +} + /* provided for a backward compatibility to WIDE-DHCPv6 before Oct 1 2006 */ nis-server-domain-name { DECHO; return (NIS_NAME); } nisp-server-domain-name { DECHO; return (NISP_NAME); } diff --git a/common.c b/common.c index a175c64..5f59c8f 100644 --- a/common.c +++ b/common.c @@ -114,6 +114,104 @@ static ssize_t gethwid(char *, int, const char *, uint16_t *); static char *sprint_uint64(char *, int, uint64_t); static char *sprint_auth(struct dhcp6_optinfo *); +/* XXX */ +int +rawop_count_list(head) + struct rawop_list *head; +{ + struct rawoption *op; + int i; + + //dprintf(LOG_INFO, FNAME, "counting list at %p", (void*)head); + + for (i = 0, op = TAILQ_FIRST(head); op; op = TAILQ_NEXT(op, link)) { + i++; + } + + return (i); +} + +void +rawop_clear_list(head) + struct rawop_list *head; +{ + struct rawoption *op; + + //dprintf(LOG_INFO, FNAME, "clearing %d rawops at %p", rawop_count_list(head), (void*)head); + + while ((op = TAILQ_FIRST(head)) != NULL) { + + //dprintf(LOG_INFO, FNAME, " current op: %p link: %p", (void*)op, op->link); + TAILQ_REMOVE(head, op, link); + + if (op->data != NULL) { + dprintf(LOG_INFO, FNAME, " freeing op data at %p", (void*)op->data); + free(op->data); + } + free(op); // Needed? + } + return; +} + +int +rawop_copy_list(dst, src) + struct rawop_list *dst, *src; +{ + struct rawoption *op, *newop; + + /* + dprintf(LOG_INFO, FNAME, + " copying rawop list %p to %p (%d ops)", + (void*)src, (void*)dst, rawop_count_list(src)); + */ + + for (op = TAILQ_FIRST(src); op; op = TAILQ_NEXT(op, link)) { + newop = NULL; + if ((newop = malloc(sizeof(*newop))) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to allocate memory for a new raw option"); + goto fail; + } + memset(newop, 0, sizeof(*newop)); + + newop->opnum = op->opnum; + newop->datalen = op->datalen; + newop->data = NULL; + + /* copy data */ + if ((newop->data = malloc(newop->datalen)) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to allocate memory for new raw option data"); + goto fail; + } + memcpy(newop->data, op->data, newop->datalen); + //dprintf(LOG_INFO, FNAME, " copied %d bytes of data at %p", newop->datalen, (void*)newop->data); + + TAILQ_INSERT_TAIL(dst, newop, link); + } + return (0); + + fail: + rawop_clear_list(dst); + return (-1); +} + +void +rawop_move_list(dst, src) + struct rawop_list *dst, *src; +{ + struct rawoption *op; + /* + dprintf(LOG_INFO, FNAME, + " moving rawop list of %d from %p to %p", + rawop_count_list(src), (void*)src, (void*)dst); + */ + while ((op = TAILQ_FIRST(src)) != NULL) { + TAILQ_REMOVE(src, op, link); + TAILQ_INSERT_TAIL(dst, op, link); + } +} + int dhcp6_copy_list(struct dhcp6_list *dst, struct dhcp6_list *src) { @@ -1318,7 +1422,10 @@ dhcp6_init_options(optinfo) TAILQ_INIT(&optinfo->nispname_list); TAILQ_INIT(&optinfo->bcmcs_list); TAILQ_INIT(&optinfo->bcmcsname_list); + /* XXX */ + TAILQ_INIT(&optinfo->rawops); + optinfo->authproto = DHCP6_AUTHPROTO_UNDEF; optinfo->authalgorithm = DHCP6_AUTHALG_UNDEF; optinfo->authrdm = DHCP6_AUTHRDM_UNDEF; @@ -1362,6 +1469,9 @@ dhcp6_clear_options(optinfo) if (optinfo->ifidopt_id != NULL) free(optinfo->ifidopt_id); + /* XXX */ + rawop_clear_list(&optinfo->rawops); + dhcp6_init_options(optinfo); } @@ -1410,6 +1524,9 @@ dhcp6_copy_options(dst, src) dst->elapsed_time = src->elapsed_time; dst->refreshtime = src->refreshtime; dst->pref = src->pref; + + /* XXX */ + rawop_copy_list(&dst->rawops, &src->rawops); if (src->relaymsg_msg != NULL) { if ((dst->relaymsg_msg = malloc(src->relaymsg_len)) == NULL) @@ -1478,6 +1599,9 @@ dhcp6_get_options(p, ep, optinfo) struct dhcp6_ia ia; struct dhcp6_list sublist; int authinfolen; + + /* XXX */ + struct rawoption *rawop; bp = (char *)p; for (; p + 1 <= ep; p = np) { @@ -1679,6 +1802,15 @@ dhcp6_get_options(p, ep, optinfo) case DHCP6_AUTHPROTO_RECONFIG: break; #endif + /* XXX */ + case 0: + // Discard auth + dprintf(LOG_DEBUG, FNAME, " Discarding null authentication"); + optinfo->authproto = DHCP6_AUTHPROTO_UNDEF; + optinfo->authalgorithm = DHCP6_AUTHALG_UNDEF; + optinfo->authrdm = DHCP6_AUTHRDM_UNDEF; + break; + default: d_printf(LOG_INFO, FNAME, "unsupported authentication protocol: %d", @@ -1854,6 +1986,16 @@ dhcp6_get_options(p, ep, optinfo) dhcp6_clear_list(&sublist); break; + + /* XX */ + case DHCPOPT_RAW: + rawop = (struct rawoption *) cp; + dprintf(LOG_DEBUG, FNAME, + "raw option: %d", + rawop->opnum); + TAILQ_INSERT_TAIL(&optinfo->rawops, rawop, link); + break; + default: /* no option specific behavior */ d_printf(LOG_INFO, FNAME, @@ -2230,7 +2385,9 @@ dhcp6_set_options(type, optbp, optep, optinfo) struct dhcp6_listval *stcode, *op; int len = 0, optlen; char *tmpbuf = NULL; + /* XXX */ + struct rawoption *rawop; if (optinfo->clientID.duid_len) { if (copy_option(DH6OPT_CLIENTID, optinfo->clientID.duid_len, optinfo->clientID.duid_id, &p, optep, &len) != 0) { @@ -2447,6 +2607,21 @@ dhcp6_set_options(type, optbp, optep, optinfo) } } + /* XXX */ + for (rawop = TAILQ_FIRST(&optinfo->rawops); rawop; + rawop = TAILQ_NEXT(rawop, link)) { + + dprintf(LOG_DEBUG, FNAME, + " raw option %d length %d at %p", + rawop->opnum, rawop->datalen, (void*)rawop); + + if (copy_option(rawop->opnum, rawop->datalen, + rawop->data, &p, + optep, &len) != 0) { + goto fail; + } + } + if (optinfo->authproto != DHCP6_AUTHPROTO_UNDEF) { struct dhcp6opt_auth *auth; int authlen; @@ -3027,6 +3210,11 @@ dhcp6optstr(type) return ("subscriber ID"); case DH6OPT_CLIENT_FQDN: return ("client FQDN"); + + /* XXX */ + case DHCPOPT_RAW: + return ("raw"); + default: snprintf(genstr, sizeof(genstr), "opt_%d", type); return (genstr); diff --git a/config.c b/config.c index 67ca6fc..9b375dd 100644 --- a/config.c +++ b/config.c @@ -106,6 +106,10 @@ struct dhcp6_ifconf { int server_pref; /* server preference (server only) */ char *scriptpath; /* path to config script (client only) */ + + /* XXX */ + struct duid duid; + struct rawop_list rawops; struct dhcp6_list reqopt_list; struct ia_conflist iaconf_list; @@ -179,8 +187,11 @@ configure_interface(iflist) ifc->server_pref = DH6OPT_PREF_UNDEF; TAILQ_INIT(&ifc->reqopt_list); TAILQ_INIT(&ifc->iaconf_list); + /* XXX */ + TAILQ_INIT(&ifc->rawops); + for (cfl = ifp->params; cfl; cfl = cfl->next) { switch(cfl->type) { case DECL_REQUEST: if (dhcp6_mode != DHCP6_MODE_CLIENT) { @@ -207,6 +221,22 @@ configure_interface(iflist) goto bad; } break; + /* XXX */ + case DECL_DUID: + if ((configure_duid((char *)cfl->ptr, + &ifc->duid)) != 0) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "failed to configure " + "DUID for %s", + configfilename, cfl->line, + ifc->ifname); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, + "configure DUID for %s: %s", + ifc->ifname, duidstr(&ifc->duid)); + break; + case DECL_INFO_ONLY: if (dhcp6_mode != DHCP6_MODE_CLIENT) { d_printf(LOG_INFO, FNAME, "%s:%d " @@ -1303,14 +1333,28 @@ configure_commit() struct dhcp6_ifconf *ifc; struct dhcp6_if *ifp; struct ia_conf *iac; + /* XXX */ + struct rawoption *rawop; + + static int init = 1; + /* commit interface configuration */ for (ifp = dhcp6_if; ifp; ifp = ifp->next) { /* re-initialization */ ifp->send_flags = 0; ifp->allow_flags = 0; dhcp6_clear_list(&ifp->reqopt_list); clear_iaconf(&ifp->iaconf_list); + + /* XXX */ + if (init) { + TAILQ_INIT(&ifp->rawops); + init = 0; + } else { + rawop_clear_list(&ifp->rawops); + } + ifp->server_pref = DH6OPT_PREF_UNDEF; if (ifp->scriptpath != NULL) free(ifp->scriptpath); @@ -1346,8 +1399,26 @@ configure_commit() } ifp->pool = ifc->pool; ifc->pool.name = NULL; - } + + /* XXX */ + if (ifc->duid.duid_id != NULL) { + dprintf(LOG_INFO, FNAME, "copying duid"); + duidcpy(&ifp->duid, &ifc->duid); + } + dprintf(LOG_DEBUG, + "conf_commit: copying %d rawops from %p (ifc) to %p (ifp)", + rawop_count_list(&ifc->rawops), &ifc->rawops, &ifp->rawops); + rawop_clear_list(&ifp->rawops); + rawop_copy_list(&ifp->rawops, &ifc->rawops); // XXX: breaks if move instead of copy + + + } + + /* XXX*/ + rawop_clear_list(&ifc->rawops); + duidfree(&ifc->duid); + clear_ifconf(dhcp6_ifconflist); dhcp6_ifconflist = NULL; @@ -1546,7 +1627,11 @@ add_options(opcode, ifc, cfl0) int opttype; struct authinfo *ainfo; struct ia_conf *iac; + + /* XXX */ + char *cp; + struct rawoption *rawopc; for (cfl = cfl0; cfl; cfl = cfl->next) { switch(cfl->type) { case DHCPOPT_RAPID_COMMIT: @@ -1639,6 +1723,17 @@ add_options(opcode, ifc, cfl0) break; } break; + + /* XXX */ + case DHCPOPT_RAW: + opttype = DHCPOPT_RAW; + rawopc = (struct rawoption *) cfl->ptr; + dprintf(LOG_INFO, FNAME, + "add raw option: %d length: %d", + rawopc->opnum, rawopc->datalen); + TAILQ_INSERT_TAIL(&ifc->rawops, rawopc, link); + break; + case DHCPOPT_SIP: case DHCPOPT_SIPNAME: case DHCPOPT_DNS: diff --git a/config.h b/config.h index fe60404..7c4cc9c 100644 --- a/config.h +++ b/config.h @@ -83,6 +83,10 @@ struct dhcp6_if { struct dhcp6_poolspec pool; /* address pool (server only) */ char *scriptpath; /* path to config script (client only) */ + /* XXX */ + struct duid duid; + struct rawop_list rawops; + struct dhcp6_list reqopt_list; struct ia_conflist iaconf_list; @@ -282,7 +287,10 @@ enum { DECL_SEND, DECL_ALLOW, DECL_INFO_ONLY, DECL_REQUEST, DECL_DUID, IACONF_PIF, IACONF_PREFIX, IACONF_ADDR, DHCPOPT_SIP, DHCPOPT_SIPNAME, AUTHPARAM_PROTO, AUTHPARAM_ALG, AUTHPARAM_RDM, AUTHPARAM_KEY, - KEYPARAM_REALM, KEYPARAM_KEYID, KEYPARAM_SECRET, KEYPARAM_EXPIRE }; + KEYPARAM_REALM, KEYPARAM_KEYID, KEYPARAM_SECRET, KEYPARAM_EXPIRE, + /* XXX */ + DHCPOPT_RAW + }; typedef enum {DHCP6_MODE_SERVER, DHCP6_MODE_CLIENT, DHCP6_MODE_RELAY } dhcp6_mode_t; diff --git a/dhcp6.h b/dhcp6.h index 2d33399..c4acb9a 100644 --- a/dhcp6.h +++ b/dhcp6.h @@ -95,6 +95,16 @@ #define DHCP6_IRT_DEFAULT 86400 /* 1 day */ #define DHCP6_IRT_MINIMUM 600 +/* XXX */ +TAILQ_HEAD(rawop_list, rawoption); +struct rawoption { + TAILQ_ENTRY(rawoption) link; + + int opnum; + char *data; + int datalen; +}; + /* DUID: DHCP unique Identifier */ struct duid { size_t duid_len; /* length */ @@ -185,6 +195,9 @@ struct dhcp6_optinfo { struct dhcp6_list bcmcs_list; /* BCMC server list */ struct dhcp6_list bcmcsname_list; /* BCMC domain list */ + /* XXX */ + struct rawop_list rawops; + struct dhcp6_vbuf relay_msg; /* relay message */ #define relaymsg_len relay_msg.dv_len #define relaymsg_msg relay_msg.dv_buf diff --git a/dhcp6c.c b/dhcp6c.c index 3c5a488..6ae36b1 100644 --- a/dhcp6c.c +++ b/dhcp6c.c @@ -1142,6 +1142,8 @@ client6_send(struct dhcp6_event *ev) struct dhcp6_optinfo optinfo; ssize_t optlen, len; struct dhcp6_eventdata *evd; + /* XXX */ + struct rawoption *rawop; ifp = ev->ifp; @@ -1301,6 +1303,10 @@ client6_send(struct dhcp6_event *ev) goto end; } + /* XXX */ + rawop_clear_list(&optinfo.rawops); + rawop_copy_list(&optinfo.rawops, &ifp->rawops); + /* set options in the message */ if ((optlen = dhcp6_set_options(dh6->dh6_msgtype, (struct dhcp6opt *)(dh6 + 1),