Basic packet handling works. Need to add niceties and then all of the handshake logic.
A => MODULE/Protocols.pmod/DHCPv6.pmod/AdvertiseMessage.pike +3 -0
@@ 0,0 1,3 @@ 
+inherit .DHCPResponseMessage;
+
+constant message_type = .MESSAGE_ADVERTISE;

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/ClientIdOption.pike +17 -0
@@ 0,0 1,17 @@ 
+inherit .DHCPOption;
+
+constant option_type = 1;
+
+.DUID duid;
+
+protected variant void create(.DUID _duid) {
+  duid = _duid;
+}
+
+void encode_body(Stdio.Buffer buf) {
+  buf->add(duid->encode());
+}
+
+void decode_body(Stdio.Buffer buf) {
+  duid = .DUID(buf->read());
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/DHCPMessage.pike +60 -0
@@ 0,0 1,60 @@ 
+constant message_type = 0;
+int transaction_id;
+
+array(.DHCPOption) options;
+
+inherit ADT.struct;
+
+protected variant void create(void|string(8bit) s) {
+  ::create(s);
+  decode();
+}
+
+protected variant void create(int _transaction_id) {
+  transaction_id = _transaction_id;
+}
+
+protected void decode() {
+  transaction_id = read_int(3);
+  decode_body(this);
+}
+
+protected void decode_body(Stdio.Buffer buf) {  
+  options = ({});
+
+  object option; 
+  while(sizeof(this)) {
+    option = decode_option(buf);
+  }
+  if(option) options += ({option});
+}
+
+object decode_option(Stdio.Buffer buf) {
+  int option_type = buf->read_int(2);
+
+  program p = Protocols.DHCPv6.get_option_for_type(option_type);
+ //= Protocols.DHCPv6.option_type_mapping[option_type];
+
+  if(!p) throw(Error.Generic("Invalid DHCP Option type " + option_type +".\n"));
+werror("p: %O\n", p);
+  object option;
+  string s = buf->read_hstring(2);
+  werror("s: %O\n", s);
+  option = p(s);
+  return option;
+}
+
+protected void encode_body(Stdio.Buffer buf) {
+
+  foreach(options;; object option) {
+     buf->add(option->encode());
+  }
+}
+
+string encode() {
+  clear();
+  add_int(message_type, 1);    
+  add_int(transaction_id, 3);
+  encode_body(this);
+  return read();
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/DHCPOption.pike +32 -0
@@ 0,0 1,32 @@ 
+constant option_type = 0;
+
+inherit ADT.struct;
+
+protected variant void create(void|string(8bit) s) {
+  ::create(s);
+werror("%O(%O)\n", this, s);
+  decode();
+}
+
+protected void decode() {
+werror("%O->decode()\n", this);
+  decode_body(this);
+}
+
+protected void decode_body(Stdio.Buffer buffer) {  
+  werror("base DHCPOption decode(%O)\n", buffer);
+}
+
+protected void encode_body(Stdio.Buffer buffer) {
+}
+
+string encode() {
+  clear();
+  add_int(option_type, 2);
+  Stdio.Buffer b = Stdio.Buffer();    
+  encode_body(b);
+werror("option type %O, length %O\n", option_type, sizeof(b));
+  add_int(sizeof(b), 2);
+  add(b->read());
+  return read();
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/DHCPResponseMessage.pike +2 -0
@@ 0,0 1,2 @@ 
+inherit .DHCPMessage;
+

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/DUID.pike +15 -0
@@ 0,0 1,15 @@ 
+string duid;
+
+protected variant void create(string _duid) {
+  duid = _duid;
+}
+
+// Generate a DUID Assigned by Vendor Based on Enterprise Number [DUID-EN]
+protected variant void create(int enterprise_num, string enterprise_id) {
+  if(!enterprise_num) enterprise_num = 13047; // Welliver enterprises
+  duid = sprintf("%2c%4c%s", 2, enterprise_num, enterprise_id);
+}
+
+string encode() {
+  return duid;
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/ElapsedTimeOption.pike +21 -0
@@ 0,0 1,21 @@ 
+inherit .DHCPOption;
+
+constant option_type = 8;
+
+int elapsed_time;
+
+protected variant void create(int since) {
+  if(!since) elapsed_time = 0;
+  else {
+    elapsed_time = (time() - since) * 100; // hundredths of a second
+    if(elapsed_time > 0xffff) elapsed_time = 0xffff;
+  }
+}
+
+void encode_body(Stdio.Buffer buf) {
+  buf->add_int(elapsed_time, 2);
+}
+
+void decode_body(Stdio.Buffer buf) {
+  elapsed_time = buf->read_int(2);
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/IAID.pike +9 -0
@@ 0,0 1,9 @@ 
+string iaid;
+
+protected variant void create(string _iaid) {
+  iaid = _iaid;
+}
+
+string encode() {
+  return sprintf("%04s", iaid);
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/IAPD.pike +58 -0
@@ 0,0 1,58 @@ 
+inherit .DHCPOption;
+constant IA_PD_OPTION = 26;
+
+constant option_type = 25;
+.IAID iaid;
+int t1;
+int t2;
+
+array options;
+
+protected variant void create(.IAID _iaid, int t1_secs, int t2_secs, array _options) {
+  iaid = _iaid;
+  t1 = t1_secs;
+  t2 = t2_secs;
+  options = _options;
+}
+
+protected void encode_body(Stdio.Buffer buf) {
+  buf->add(iaid->encode());
+  buf->add_int(t1, 4);
+  buf->add_int(t2, 4);
+  foreach(options;; object option) {
+mixed e = option->encode();
+werror("OPTION: %O => %O\n", option, e);
+     buf->add(e);
+  }
+}
+
+protected void decode_body(Stdio.Buffer buf) {
+werror("parsing IAPD\n");
+  iaid = .IAID(buf->read(4));
+  t1 = buf->read_int(4);
+  t2 = buf->read_int(4);
+
+  options = ({});
+  object option;
+
+  while(sizeof(buf)) {
+   option = decode_pd_option(buf);
+ 
+    if(option) options +=({option});
+  }
+}
+
+object decode_pd_option(Stdio.Buffer buf) {
+//  object rk = buf->rewind_key();
+
+  int pd_type = buf->read_int(2);
+
+  if(pd_type != IA_PD_OPTION) {
+     throw(Error.Generic("Received invalid IA_PD option type " + pd_type +".\n"));
+  }
+//  rk->rewind();
+//  rk = 0;
+
+  object option = .IA_PDOption(buf->read_hstring(2));
+  return option;
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/IA_PDOption.pike +53 -0
@@ 0,0 1,53 @@ 
+inherit .DHCPOption;
+
+constant option_type = 26;
+
+int preferred_lifetime;
+int valid_lifetime;
+
+int prefix;
+string address;
+
+array options;
+
+protected variant void create(int _preferred_lifetime, int _valid_lifetime, int _prefix, string|int(0..0) _address) {
+  preferred_lifetime = _preferred_lifetime;
+  valid_lifetime = _valid_lifetime;
+  prefix = _prefix;
+//if(_address && sizeof(address) != 16) throw(Error.Generic("Invalid address: %O\n", _address));
+  address = _address;
+}
+
+void decode_body(Stdio.Buffer buf) {
+  preferred_lifetime = buf->read_int(4);
+  valid_lifetime = buf->read_int(4);
+  prefix = buf->read_int(1);
+  address = Protocols.IPv6.format_addr_short(array_sscanf(buf->read(16), "%2c"*8));
+  options = ({});
+  while(sizeof(buf)) {
+    object option = decode_option(buf);
+    if(option) options += ({option});
+  }
+}
+
+object decode_option(Stdio.Buffer buf) {
+  int option_type = buf->read_int(2);
+
+  program p = Protocols.DHCPv6.get_option_for_type(option_type);
+
+  if(!p) throw(Error.Generic("Invalid DHCP Option type " + option_type +".\n"));
+
+  object option = p(buf);
+
+  return option;
+}
+
+void encode_body(Stdio.Buffer buf) {
+  buf->add_int(3600*24, 4);
+  buf->add_int(3600*36, 4);
+  buf->add_int(prefix, 1);
+  if(!address)
+    buf->add_int(0, 16);
+  else
+    buf->add_ints(Protocols.IPv6.parse_addr(address), 2);
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/OptionRequestOption.pike +31 -0
@@ 0,0 1,31 @@ 
+inherit .DHCPOption;
+
+constant option_type = 6;
+array(array(int)) options;
+
+protected void create(array(array(int)) _options) {
+  options = _options;
+}
+
+protected variant void create() {
+  options = ({});
+}
+
+void encode_body(Stdio.Buffer buf) {
+
+  foreach(options;; array option) {
+     if(sizeof(option) != 2) throw(Error.Generic("OptionRequestOptions must be a multiple of 2.\n"));
+     buf->add_int(option[0], 2);
+     buf->add_int(option[1], 2);
+  }
+}
+
+void decode_body(Stdio.Buffer buf) {
+  options = ({});
+  while(sizeof(buf)) {
+    array(int) option = allocate(2);
+    option[0] = buf->read_int(2);
+    option[1] = buf->read_int(2);
+    options+=({ option });
+  }
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/ReleaseMessage.pike +3 -0
@@ 0,0 1,3 @@ 
+inherit .DHCPResponseMessage;
+
+constant message_type = .MESSAGE_RELEASE;

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/RenewMessage.pike +3 -0
@@ 0,0 1,3 @@ 
+inherit .DHCPResponseMessage;
+
+constant message_type = .MESSAGE_RENEW;

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/RequestMessage.pike +3 -0
@@ 0,0 1,3 @@ 
+inherit .DHCPResponseMessage;
+
+constant message_type = .MESSAGE_REQUEST;

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/ServerIdOption.pike +17 -0
@@ 0,0 1,17 @@ 
+inherit .DHCPOption;
+
+constant option_type = 2;
+
+.DUID duid;
+
+protected variant void create(.DUID _duid) {
+  duid = _duid;
+}
+
+void encode_body(Stdio.Buffer buf) {
+  buf->add(duid->encode());
+}
+
+void decode_body(Stdio.Buffer buf) {
+  duid = .DUID(buf->read());
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/SolicitMessage.pike +4 -0
@@ 0,0 1,4 @@ 
+inherit .DHCPMessage;
+
+constant message_type = .MESSAGE_SOLICIT;
+

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/StatusCodeOption.pike +21 -0
@@ 0,0 1,21 @@ 
+inherit .DHCPOption;
+
+constant option_type = 13;
+
+int status_code;
+string status_message;
+
+protected variant void create(int code, string message) {
+   status_code = code;
+   status_message = message;
+}
+
+void encode_body(Stdio.Buffer buf) {
+  buf->add_int(status_code, 2);
+  buf->add(string_to_utf8(status_message));
+}
+
+void decode_body(Stdio.Buffer buf) {
+  status_code = buf->read_int(2);
+  status_message = utf8_to_string(buf->read());
+}

          
A => MODULE/Protocols.pmod/DHCPv6.pmod/module.pmod +49 -0
@@ 0,0 1,49 @@ 
+mapping(int:program) option_type_mapping = ([]);
+mapping(int:program) message_type_mapping = ([]);
+
+constant MESSAGE_SOLICIT = 1;
+constant MESSAGE_ADVERTISE = 2;
+constant MESSAGE_REQUEST = 3;
+constant MESSAGE_CONFIRM = 4;
+constant MESSAGE_RENEW =  5;
+constant MESSAGE_REBIND = 6;
+constant MESSAGE_REPLY = 7;
+constant MESSAGE_RELEASE = 8;
+constant MESSAGE_DECLINE = 9;
+constant MESSAGE_RECONFIGURE = 10;
+constant MESSAGE_INFORMATION_REQUEST = 11;
+constant MESSAGE_RELAY_FORW = 12;
+constant MESSAGE_RELAY_REPL = 13;
+
+protected void create() {
+  foreach(values(Protocols.DHCPv6);; mixed p) {
+    if(!programp(p)) continue;
+    if(Program.inherits(p, .DHCPMessage) && p->message_type)
+      message_type_mapping[p->message_type] = p;
+    else if(Program.inherits(p, .DHCPOption) && p->option_type)
+      option_type_mapping[p->option_type] = p;
+  }
+}
+
+int generate_transaction_id() {
+  return random(0xffffff);
+}
+
+program get_option_for_type(int type) {
+  return option_type_mapping[type];
+}
+
+program get_message_for_type(int type) {
+  return message_type_mapping[type];
+}
+
+.DHCPMessage decode_message(string(8bit) s) {
+  if(sizeof(s) < 4) { 
+    throw(Error.Generic("Invalid DHCPv6 message. Must be at least 4 bytes in length.\n"));
+  }
+  int message_type = s[0];
+  program p = get_message_for_type(message_type);
+  if(!p) throw(Error.Generic("Unable to find a registered message type for " + message_type + ".\n"));
+
+  return p(s[1..]);
+}

          
A => dhcpv6_pd.pike +54 -0
@@ 0,0 1,54 @@ 
+
+constant DHCP_CLIENT_PORT = 546; 
+constant DHCP_SERVER_PORT = 547;
+
+constant DHCP_BROADCAST_ADDRESS = "FF02::1:2";
+
+string v6if = "net0";
+string identifier;
+
+Stdio.UDP dhcp;
+Protocols.DHCPv6.DUID duid;
+
+int main(int argc, array argv) {
+  identifier = Standards.JSON.decode(Process.popen("sysinfo"))->UUID;
+  werror("Identifier: %O\n", identifier);
+  dhcp = Stdio.UDP();
+  dhcp->bind(DHCP_CLIENT_PORT, "2001:558:6003:2a:79b0:ea90:48d2:3ad7");
+  dhcp->enable_broadcast();
+  dhcp->set_nonblocking();
+  dhcp->set_read_callback(got_packet);
+call_out(send_solicit, 5);
+  return -1;
+}
+
+void got_packet(mapping data, mixed ... args) {
+  werror("got_packet(%O, %O)\n", data, args);
+  object x = Protocols.DHCPv6.decode_message(data->data);
+  werror("x: %O options: %O\n", x, x->options[0]);
+  werror("o: %O\n", mkmapping(indices(x->options[0]), values(x->options[0])));
+}
+
+void send(Protocols.DHCPv6.DHCPMessage message, string dest, int port) {
+  string m = message->encode();
+  werror("sending message: %O -> %O on port %d\n", message, m, port);
+  dhcp->send(dest, port, m);
+}
+
+void send_broadcast(Protocols.DHCPv6.DHCPMessage message) {
+  send(message, DHCP_BROADCAST_ADDRESS, DHCP_SERVER_PORT);
+}
+
+void send_solicit() {
+  object p = Protocols.DHCPv6.SolicitMessage(Protocols.DHCPv6.generate_transaction_id());
+  object id = Protocols.DHCPv6.ClientIdOption(Protocols.DHCPv6.DUID(0, identifier));
+  object pd_opt = Protocols.DHCPv6.IA_PDOption(3600*24, 3600*36, 64, 0);
+  object ia_ident = Protocols.DHCPv6.IAID(Crypto.MD5.hash(v6if)[0..3]);
+
+  p->options += ({id});
+  p->options += ({Protocols.DHCPv6.OptionRequestOption()});
+  p->options += ({Protocols.DHCPv6.ElapsedTimeOption(0)});
+  p->options += ({Protocols.DHCPv6.IAPD(ia_ident, 3600*24, 3600*36, ({pd_opt}))
+});
+  send_broadcast(p);
+}