summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6/dhcpv6message.go
diff options
context:
space:
mode:
Diffstat (limited to 'dhcpv6/dhcpv6message.go')
-rw-r--r--dhcpv6/dhcpv6message.go180
1 files changed, 180 insertions, 0 deletions
diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go
new file mode 100644
index 0000000..4461c50
--- /dev/null
+++ b/dhcpv6/dhcpv6message.go
@@ -0,0 +1,180 @@
+package dhcpv6
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "fmt"
+ "github.com/insomniacslk/dhcp/dhcpv6/options"
+ "github.com/insomniacslk/dhcp/iana"
+ "log"
+ "net"
+ "time"
+)
+
+const MessageHeaderSize = 4
+
+type DHCPv6Message struct {
+ messageType MessageType
+ transactionID uint32 // only 24 bits are used though
+ options []options.Option
+}
+
+func BytesToTransactionID(data []byte) (*uint32, error) {
+ // return a uint32 from a sequence of bytes, representing a transaction ID.
+ // Transaction IDs are three-bytes long. If the provided data is shorter than
+ // 3 bytes, it return an error. If longer, will use the first three bytes
+ // only.
+ if len(data) < 3 {
+ return nil, fmt.Errorf("Invalid transaction ID: less than 3 bytes")
+ }
+ buf := make([]byte, 4)
+ copy(buf[1:4], data[:3])
+ tid := binary.BigEndian.Uint32(buf)
+ return &tid, nil
+}
+
+func GenerateTransactionID() (*uint32, error) {
+ var tid *uint32
+ for {
+ tidBytes := make([]byte, 4)
+ n, err := rand.Read(tidBytes)
+ if n != 4 {
+ return nil, fmt.Errorf("Invalid random sequence: shorter than 4 bytes")
+ }
+ tid, err = BytesToTransactionID(tidBytes)
+ if err != nil {
+ return nil, err
+ }
+ if tid == nil {
+ return nil, fmt.Errorf("Error: got a nil Transaction ID")
+ }
+ // retry until != 0
+ // TODO add retry limit
+ if *tid != 0 {
+ break
+ }
+ }
+ return tid, nil
+}
+
+// Return a time integer suitable for DUID-LLT, i.e. the current time counted in
+// seconds since January 1st, 2000, midnight UTC, modulo 2^32
+func GetTime() uint32 {
+ now := time.Since(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC))
+ return uint32((now.Nanoseconds() / 1000000000) % 0xffffffff)
+}
+
+// Create a new SOLICIT message with DUID-LLT, using the given network
+// interface's hardware address and current time
+func NewSolicitForInterface(ifname string) (*DHCPv6Message, error) {
+ d, err := NewMessage()
+ if err != nil {
+ return nil, err
+ }
+ d.SetMessage(SOLICIT)
+ iface, err := net.InterfaceByName(ifname)
+ if err != nil {
+ return nil, err
+ }
+ cid := options.OptClientId{}
+ cid.SetClientID(options.Duid{
+ Type: options.DUID_LLT,
+ HwType: iana.HwTypeEthernet,
+ Time: GetTime(),
+ LinkLayerAddr: iface.HardwareAddr,
+ })
+
+ d.AddOption(&cid)
+ oro := options.OptRequestedOption{}
+ oro.SetRequestedOptions([]options.OptionCode{
+ options.DNS_RECURSIVE_NAME_SERVER,
+ options.DOMAIN_SEARCH_LIST,
+ })
+ d.AddOption(&oro)
+ d.AddOption(&options.OptElapsedTime{})
+ // FIXME use real values for IA_NA
+ iaNa := options.OptIANA{}
+ iaNa.SetIAID([4]byte{0x27, 0xfe, 0x8f, 0x95})
+ iaNa.SetT1(0xe10)
+ iaNa.SetT2(0x1518)
+ d.AddOption(&iaNa)
+ return d, nil
+}
+
+func (d *DHCPv6Message) Type() MessageType {
+ return d.messageType
+}
+
+func (d *DHCPv6Message) SetMessage(messageType MessageType) {
+ if d.MessageTypeToString() == "" {
+ log.Printf("Warning: unknown DHCPv6 message type: %v", messageType)
+ }
+ d.messageType = messageType
+}
+
+func (d *DHCPv6Message) MessageTypeToString() string {
+ return MessageTypeToString(d.messageType)
+}
+
+func (d *DHCPv6Message) TransactionID() uint32 {
+ return d.transactionID
+}
+
+func (d *DHCPv6Message) SetTransactionID(tid uint32) {
+ ttid := tid & 0x00ffffff
+ if ttid != tid {
+ log.Printf("Warning: truncating transaction ID that is longer than 24 bits: %v", tid)
+ }
+ d.transactionID = ttid
+}
+
+func (d *DHCPv6Message) Options() []options.Option {
+ return d.options
+}
+
+func (d *DHCPv6Message) SetOptions(options []options.Option) {
+ d.options = options
+}
+
+func (d *DHCPv6Message) AddOption(option options.Option) {
+ d.options = append(d.options, option)
+}
+
+func (d *DHCPv6Message) String() string {
+ return fmt.Sprintf("DHCPv6Message(messageType=%v transactionID=0x%06x, %d options)",
+ d.MessageTypeToString(), d.TransactionID(), len(d.options),
+ )
+}
+
+func (d *DHCPv6Message) Summary() string {
+ ret := fmt.Sprintf(
+ "DHCPv6Message\n"+
+ " messageType=%v\n"+
+ " transactionid=0x%06x\n",
+ d.MessageTypeToString(),
+ d.TransactionID(),
+ )
+ ret += " options=["
+ if len(d.options) > 0 {
+ ret += "\n"
+ }
+ for _, opt := range d.options {
+ ret += fmt.Sprintf(" %v\n", opt.String())
+ }
+ ret += " ]\n"
+ return ret
+}
+
+// Convert a DHCPv6Message structure into its binary representation, suitable for being
+// sent over the network
+func (d *DHCPv6Message) ToBytes() []byte {
+ var ret []byte
+ ret = append(ret, byte(d.messageType))
+ tidBytes := make([]byte, 4)
+ binary.BigEndian.PutUint32(tidBytes, d.transactionID)
+ ret = append(ret, tidBytes[1:4]...) // discard the first byte
+ for _, opt := range d.options {
+ ret = append(ret, opt.ToBytes()...)
+ }
+ return ret
+}