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);
+}