mirror of
https://github.com/danderson/netboot.git
synced 2025-10-15 09:31:29 +02:00
first stab at dhcpv6 support
This commit is contained in:
parent
8834cc3e94
commit
3a5808cb30
@ -16,15 +16,30 @@ package main
|
||||
|
||||
import (
|
||||
"go.universe.tf/netboot/pixiecore"
|
||||
"go.universe.tf/netboot/pixiecore/cli"
|
||||
"go.universe.tf/netboot/third_party/ipxe"
|
||||
// "go.universe.tf/netboot/pixiecore/cli"
|
||||
// "go.universe.tf/netboot/third_party/ipxe"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// qemu-system-x86_64 -L . --bios /usr/share/edk2-firmware/ipv6/OVMF.fd -netdev bridge,br=br1,id=net0 -device virtio-net-pci,netdev=net0
|
||||
func main() {
|
||||
cli.Ipxe[pixiecore.FirmwareX86PC] = ipxe.MustAsset("undionly.kpxe")
|
||||
/*cli.Ipxe[pixiecore.FirmwareX86PC] = ipxe.MustAsset("undionly.kpxe")
|
||||
cli.Ipxe[pixiecore.FirmwareEFI32] = ipxe.MustAsset("ipxe-i386.efi")
|
||||
cli.Ipxe[pixiecore.FirmwareEFI64] = ipxe.MustAsset("ipxe-x86_64.efi")
|
||||
cli.Ipxe[pixiecore.FirmwareEFIBC] = ipxe.MustAsset("ipxe-x86_64.efi")
|
||||
cli.Ipxe[pixiecore.FirmwareX86Ipxe] = ipxe.MustAsset("ipxe.pxe")
|
||||
cli.CLI()
|
||||
*/
|
||||
|
||||
log := func(subsystem, msg string) { fmt.Printf("[%s] %s", subsystem, msg) }
|
||||
s := pixiecore.ServerV6{
|
||||
Address: "2001:db8:f00f:cafe::4/64",
|
||||
Log: log,
|
||||
Debug: log,
|
||||
}
|
||||
|
||||
err := s.Serve()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s", err)
|
||||
}
|
||||
}
|
||||
|
114
dhcp6/conn.go
Normal file
114
dhcp6/conn.go
Normal file
@ -0,0 +1,114 @@
|
||||
package dhcp6
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
"golang.org/x/net/ipv6"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type conn interface {
|
||||
io.Closer
|
||||
Recv([]byte) (b []byte, addr *net.UDPAddr, ifidx int, err error)
|
||||
Send(b []byte, addr *net.UDPAddr, ifidx int) error
|
||||
SetReadDeadline(t time.Time) error
|
||||
SetWriteDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
conn *ipv6.PacketConn
|
||||
group net.IP
|
||||
ifi *net.Interface
|
||||
listenAddress string
|
||||
listenPort string
|
||||
}
|
||||
|
||||
func NewConn(addr string) (*Conn, error) {
|
||||
ifi, err := InterfaceIndexByAddress(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group := net.ParseIP("ff02::1:2")
|
||||
c, err := net.ListenPacket("udp6", "[::]:547")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pc := ipv6.NewPacketConn(c)
|
||||
if err := pc.JoinGroup(ifi, &net.UDPAddr{IP: group}); err != nil {
|
||||
pc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := pc.SetControlMessage(ipv6.FlagSrc | ipv6.FlagDst, true); err != nil {
|
||||
pc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Conn{
|
||||
conn: pc,
|
||||
group: group,
|
||||
ifi: ifi,
|
||||
listenAddress: addr,
|
||||
listenPort: "547",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Conn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func InterfaceIndexByAddress(ifAddr string) (*net.Interface, error) {
|
||||
allIfis, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting network interface information: %s", err)
|
||||
}
|
||||
for _, ifi := range allIfis {
|
||||
addrs, err := ifi.Addrs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting network interface address information: %s", err)
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if addr.String() == ifAddr {
|
||||
return &ifi, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Couldn't find an interface with address %s", ifAddr)
|
||||
}
|
||||
|
||||
func (c *Conn) RecvDHCP() (*Packet, net.IP, error) {
|
||||
b := make([]byte, 1500)
|
||||
for {
|
||||
packetSize, rcm, _, err := c.conn.ReadFrom(b)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if c.ifi.Index != 0 && rcm.IfIndex != c.ifi.Index {
|
||||
continue
|
||||
}
|
||||
if !rcm.Dst.IsMulticast() || !rcm.Dst.Equal(c.group) {
|
||||
continue // unknown group, discard
|
||||
}
|
||||
pkt := MakePacket(b, packetSize)
|
||||
|
||||
return pkt, rcm.Src, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) SendDHCP(dst net.IP, p []byte) error {
|
||||
dstAddr, err := net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%s", dst.String() + "%en0", "546"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error resolving ipv6 address %s: %s", dst.String(), err)
|
||||
}
|
||||
_, err = c.conn.WriteTo(p, nil, dstAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error sending a reply to %s: %s", dst.String(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) SourceHardwareAddress() net.HardwareAddr {
|
||||
return c.ifi.HardwareAddr
|
||||
}
|
167
dhcp6/options.go
Normal file
167
dhcp6/options.go
Normal file
@ -0,0 +1,167 @@
|
||||
package dhcp6
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"bytes"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
OptClientId uint16 = 1 // IPMask
|
||||
OptServerId = 2 // int32
|
||||
OptIaNa = 3 // IPs
|
||||
OptIaTa = 4 // IPs
|
||||
OptIaAddr = 5 // string
|
||||
OptOro = 6 // uint16
|
||||
OptPreference = 7 // string
|
||||
OptElapsedTime = 8 // IP
|
||||
OptRelayMessage = 9 // IP
|
||||
OptAuth = 11 // []byte
|
||||
OptUnicast = 12 // IP
|
||||
OptStatusCode = 13 // uint32
|
||||
OptRapidCommit = 14 // byte
|
||||
OptUserClass = 15 // IP
|
||||
OptVendorClass = 16 // []byte
|
||||
OptVendorOpts = 17 // string
|
||||
OptInterfaceId = 18 // uint16
|
||||
OptReconfMsg = 19 // uint32
|
||||
OptReconfAccept = 20 // uint32
|
||||
OptRecursiveDns = 23 // []byte
|
||||
OptBootfileUrl = 59
|
||||
OptBootfileParam = 60 //[][]byte
|
||||
OptClientArchType = 61 //[][]byte, sent by the client
|
||||
// 24? Domain search list
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Id uint16
|
||||
Length uint16
|
||||
Value []byte
|
||||
}
|
||||
|
||||
type Options map[uint16]*Option
|
||||
|
||||
func MakeOptions(bs []byte) (Options, error) {
|
||||
to_ret := make(Options)
|
||||
for len(bs) > 0 {
|
||||
optionLength := uint16(binary.BigEndian.Uint16(bs[2:4]))
|
||||
optionId := uint16(binary.BigEndian.Uint16(bs[0:2]))
|
||||
switch optionId {
|
||||
// parse client_id
|
||||
// parse server_id
|
||||
// parse IaNa # do I need to support IaTa?
|
||||
//parse ipaddr
|
||||
case OptOro:
|
||||
if optionLength% 2 != 0 {
|
||||
return nil, fmt.Errorf("OptionID request for options (6) length should be even number of bytes: %d", optionLength)
|
||||
}
|
||||
default:
|
||||
if len(bs[4:]) < int(optionLength) {
|
||||
fmt.Printf("option %d claims to have %d bytes of payload, but only has %d bytes", optionId, optionLength, len(bs[4:]))
|
||||
return nil, fmt.Errorf("option %d claims to have %d bytes of payload, but only has %d bytes", optionId, optionLength, len(bs[4:]))
|
||||
}
|
||||
}
|
||||
to_ret[optionId] = &Option{ Id: optionId, Length: optionLength, Value: bs[4 : 4+optionLength]}
|
||||
bs = bs[4+optionLength:]
|
||||
}
|
||||
return to_ret, nil
|
||||
}
|
||||
|
||||
func (o Options) HumanReadable() []string {
|
||||
to_ret := make([]string, 0, len(o))
|
||||
for _, opt := range(o) {
|
||||
to_ret = append(to_ret, fmt.Sprintf("Option: %d | %d | %d | %s\n", opt.Id, opt.Length, opt.Value, opt.Value))
|
||||
}
|
||||
return to_ret
|
||||
}
|
||||
|
||||
func (o Options) AddOption(option *Option) {
|
||||
o[option.Id] = option
|
||||
}
|
||||
|
||||
func MakeIaNaOption(iaid []byte, t1, t2 uint32, iaAddr *Option) (*Option) {
|
||||
serializedIaAddr, _ := iaAddr.Marshal()
|
||||
value := make([]byte, 12 + len(serializedIaAddr))
|
||||
copy(value[0:], iaid[0:4])
|
||||
binary.BigEndian.PutUint32(value[4:], t1)
|
||||
binary.BigEndian.PutUint32(value[8:], t2)
|
||||
copy(value[12:], serializedIaAddr)
|
||||
return &Option{Id: OptIaNa, Length: uint16(len(value)), Value: value}
|
||||
}
|
||||
|
||||
func MakeIaAddrOption(addr net.IP, preferredLifetime, validLifetime uint32) (*Option) {
|
||||
value := make([]byte, 24)
|
||||
copy(value[0:], addr)
|
||||
binary.BigEndian.PutUint32(value[16:], preferredLifetime)
|
||||
binary.BigEndian.PutUint32(value[20:], validLifetime)
|
||||
return &Option{ Id: OptIaAddr, Length: uint16(len(value)), Value: value}
|
||||
}
|
||||
|
||||
func (o Options) Marshal() ([]byte, error) {
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, 1446))
|
||||
for _, v := range(o) {
|
||||
serialized, err := v.Marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error serializing option value: %s", err)
|
||||
}
|
||||
if err := binary.Write(buffer, binary.BigEndian, serialized); err != nil {
|
||||
return nil, fmt.Errorf("Error serializing option value: %s", err)
|
||||
}
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (o *Option) Marshal() ([]byte, error) {
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, o.Length + 2))
|
||||
|
||||
err := binary.Write(buffer, binary.BigEndian, o.Id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error serializing option id: %s", err)
|
||||
}
|
||||
err = binary.Write(buffer, binary.BigEndian, o.Length)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error serializing option length: %s", err)
|
||||
}
|
||||
err = binary.Write(buffer, binary.BigEndian, o.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error serializing option value: %s", err)
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (o Options) UnmarshalOptionRequestOption() map[uint16]bool {
|
||||
oro_content := o[OptOro].Value
|
||||
to_ret := make(map[uint16]bool)
|
||||
|
||||
for i := 0; i < int(o[OptOro].Length)/2; i++ {
|
||||
to_ret[uint16(binary.BigEndian.Uint16(oro_content[i*2:(i+1)*2]))] = true
|
||||
}
|
||||
return to_ret
|
||||
}
|
||||
|
||||
func (o Options) RequestedBootFileUrlOption() bool {
|
||||
requested_options := o.UnmarshalOptionRequestOption()
|
||||
_, present := requested_options[OptBootfileUrl]
|
||||
return present
|
||||
}
|
||||
|
||||
func (o Options) HasClientId() bool {
|
||||
_, present := o[OptClientId]
|
||||
return present
|
||||
}
|
||||
|
||||
func (o Options) HasServerId() bool {
|
||||
_, present := o[OptServerId]
|
||||
return present
|
||||
}
|
||||
|
||||
func (o Options) HasIaNa() bool {
|
||||
_, present := o[OptIaNa]
|
||||
return present
|
||||
}
|
||||
|
||||
func (o Options) HasIaTa() bool {
|
||||
_, present := o[OptIaTa]
|
||||
return present
|
||||
}
|
37
dhcp6/options_test.go
Normal file
37
dhcp6/options_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package dhcp6
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
|
||||
func TestUnmarshalFailsIfOROLengthIsOdd(t *testing.T) {
|
||||
in := []byte{0, 6, 0, 3, 0, 1, 1}
|
||||
if _, err := MakeOptions(in); err == nil {
|
||||
t.Fatalf("Parsing options should fail: option request for options has odd length.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalORO(t *testing.T) {
|
||||
options := Options{
|
||||
6: [][]byte{{0, 1, 0, 5, 1, 0}},
|
||||
}
|
||||
parsed_options := options.UnmarshalOptionRequestOption()
|
||||
|
||||
if l := len(parsed_options); l != 3 {
|
||||
t.Fatalf("Expected 3 options, got: %d", l)
|
||||
}
|
||||
|
||||
if _, present := parsed_options[1]; !present {
|
||||
t.Fatalf("Should contain option id 1")
|
||||
}
|
||||
|
||||
if _, present := parsed_options[5]; !present {
|
||||
t.Fatalf("Should contain option id 5")
|
||||
}
|
||||
|
||||
if _, present := parsed_options[256]; !present {
|
||||
t.Fatalf("Should contain option id 256")
|
||||
}
|
||||
}
|
215
dhcp6/packet.go
Normal file
215
dhcp6/packet.go
Normal file
@ -0,0 +1,215 @@
|
||||
package dhcp6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"encoding/binary"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type MessageType uint8
|
||||
|
||||
const (
|
||||
MsgSolicit MessageType = iota + 1
|
||||
MsgAdvertise
|
||||
MsgRequest
|
||||
MsgConfirm
|
||||
MsgRenew
|
||||
MsgRebind
|
||||
MsgReply
|
||||
MsgRelease
|
||||
MsgDecline
|
||||
MsgReconfigure
|
||||
MsgInformationRequest
|
||||
MsgRelayForw
|
||||
MsgRelayRepl
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
Type MessageType
|
||||
TransactionID [3]byte
|
||||
Options []byte
|
||||
}
|
||||
|
||||
func MakePacket(bs []byte, len int) *Packet {
|
||||
ret := &Packet{Type: MessageType(bs[0]), Options: make([]byte, len - 4)}
|
||||
copy(ret.TransactionID[:], bs[1:4])
|
||||
copy(ret.Options[:], bs[4:len])
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *Packet) UnmarshalOptions() (Options, error) {
|
||||
ret, err := MakeOptions(p.Options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("packet has malformed options section: %s", err)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Packet) BuildResponse(serverDuid []byte) ([]byte, error) {
|
||||
switch p.Type {
|
||||
case MsgSolicit:
|
||||
return p.BuildMsgAdvertise(serverDuid)
|
||||
case MsgRequest:
|
||||
return p.BuildMsgReply(serverDuid)
|
||||
case MsgInformationRequest:
|
||||
return p.BuildMsgInformationRequestReply(serverDuid)
|
||||
case MsgRelease:
|
||||
return p.BuildMsgReleaseReply(serverDuid)
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Packet) BuildMsgAdvertise(serverDuid []byte) ([]byte, error) {
|
||||
in_options, _ := p.UnmarshalOptions()
|
||||
ret_options := make(Options)
|
||||
|
||||
ret_options.AddOption(&Option{Id: OptClientId, Length: uint16(len(in_options[OptClientId].Value)), Value: in_options[OptClientId].Value})
|
||||
ret_options.AddOption(MakeIaNaOption(in_options[OptIaNa].Value[0:4], 0, 0,
|
||||
MakeIaAddrOption(net.ParseIP("2001:db8:f00f:cafe::99"), 27000, 43200)))
|
||||
ret_options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverDuid)), Value: serverDuid})
|
||||
|
||||
if 0x10 == binary.BigEndian.Uint16(in_options[OptClientArchType].Value) { // HTTPClient
|
||||
ret_options.AddOption(&Option{Id: OptVendorClass, Length: 16, Value: []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116}}) // HTTPClient
|
||||
ret_options.AddOption(&Option{Id: OptBootfileUrl, Length: 42, Value: []byte("http://[2001:db8:f00f:cafe::4]/bootx64.efi")})
|
||||
} else {
|
||||
ret_options.AddOption(&Option{Id: OptBootfileUrl, Length: 42, Value: []byte("http://[2001:db8:f00f:cafe::4]/script.ipxe")})
|
||||
}
|
||||
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
|
||||
//ret_options.AddOption(OptBootfileParam, []byte("http://")
|
||||
//ret.Options[OptPreference] = [][]byte("http://")
|
||||
|
||||
marshalled_ret_options, _ := ret_options.Marshal()
|
||||
|
||||
ret := make([]byte, len(marshalled_ret_options) + 4, len(marshalled_ret_options) + 4)
|
||||
ret[0] = byte(MsgAdvertise)
|
||||
copy(ret[1:], p.TransactionID[:])
|
||||
copy(ret[4:], marshalled_ret_options)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// TODO: OptClientArchType may not be present
|
||||
|
||||
func (p *Packet) BuildMsgReply(serverDuid []byte) ([]byte, error) {
|
||||
in_options, _ := p.UnmarshalOptions()
|
||||
ret_options := make(Options)
|
||||
|
||||
ret_options.AddOption(&Option{Id: OptClientId, Length: uint16(len(in_options[OptClientId].Value)), Value: in_options[OptClientId].Value})
|
||||
ret_options.AddOption(MakeIaNaOption(in_options[OptIaNa].Value[0:4], 0, 0,
|
||||
MakeIaAddrOption(net.ParseIP("2001:db8:f00f:cafe::99"), 27000, 43200)))
|
||||
ret_options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverDuid)), Value: serverDuid})
|
||||
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
|
||||
if 0x10 == binary.BigEndian.Uint16(in_options[OptClientArchType].Value) { // HTTPClient
|
||||
ret_options.AddOption(&Option{Id: OptVendorClass, Length: 16, Value: []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116}}) // HTTPClient
|
||||
ret_options.AddOption(&Option{Id: OptBootfileUrl, Length: 42, Value: []byte("http://[2001:db8:f00f:cafe::4]/bootx64.efi")})
|
||||
} else {
|
||||
ret_options.AddOption(&Option{Id: OptBootfileUrl, Length: 42, Value: []byte("http://[2001:db8:f00f:cafe::4]/script.ipxe")})
|
||||
}
|
||||
marshalled_ret_options, _ := ret_options.Marshal()
|
||||
|
||||
ret := make([]byte, len(marshalled_ret_options) + 4, len(marshalled_ret_options) + 4)
|
||||
ret[0] = byte(MsgReply)
|
||||
copy(ret[1:], p.TransactionID[:])
|
||||
copy(ret[4:], marshalled_ret_options)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Packet) BuildMsgInformationRequestReply(serverDuid []byte) ([]byte, error) {
|
||||
in_options, _ := p.UnmarshalOptions()
|
||||
ret_options := make(Options)
|
||||
|
||||
ret_options.AddOption(&Option{Id: OptClientId, Length: uint16(len(in_options[OptClientId].Value)), Value: in_options[OptClientId].Value})
|
||||
ret_options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverDuid)), Value: serverDuid})
|
||||
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
|
||||
if 0x10 == binary.BigEndian.Uint16(in_options[OptClientArchType].Value) { // HTTPClient
|
||||
ret_options.AddOption(&Option{Id: OptVendorClass, Length: 16, Value: []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116}}) // HTTPClient
|
||||
ret_options.AddOption(&Option{Id: OptBootfileUrl, Length: 42, Value: []byte("http://[2001:db8:f00f:cafe::4]/bootx64.efi")})
|
||||
} else {
|
||||
ret_options.AddOption(&Option{Id: OptBootfileUrl, Length: 42, Value: []byte("http://[2001:db8:f00f:cafe::4]/script.ipxe")})
|
||||
}
|
||||
marshalled_ret_options, _ := ret_options.Marshal()
|
||||
|
||||
ret := make([]byte, len(marshalled_ret_options) + 4, len(marshalled_ret_options) + 4)
|
||||
ret[0] = byte(MsgReply)
|
||||
copy(ret[1:], p.TransactionID[:])
|
||||
copy(ret[4:], marshalled_ret_options)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Packet) BuildMsgReleaseReply(serverDuid []byte) ([]byte, error){
|
||||
in_options, _ := p.UnmarshalOptions()
|
||||
ret_options := make(Options)
|
||||
|
||||
ret_options.AddOption(&Option{Id: OptClientId, Length: uint16(len(in_options[OptClientId].Value)), Value: in_options[OptClientId].Value})
|
||||
ret_options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverDuid)), Value: serverDuid})
|
||||
v := make([]byte, 19, 19)
|
||||
copy(v[2:], []byte("Release received."))
|
||||
ret_options.AddOption(&Option{Id: OptStatusCode, Length: uint16(len(v)), Value: v})
|
||||
marshalled_ret_options, _ := ret_options.Marshal()
|
||||
|
||||
ret := make([]byte, len(marshalled_ret_options) + 4, len(marshalled_ret_options) + 4)
|
||||
ret[0] = byte(MsgReply)
|
||||
copy(ret[1:], p.TransactionID[:])
|
||||
copy(ret[4:], marshalled_ret_options)
|
||||
//copy(ret.Options, marshalled_ret_options)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Packet) ShouldDiscard(serverDuid []byte) error {
|
||||
switch p.Type {
|
||||
case MsgSolicit:
|
||||
return ShouldDiscardSolicit(p)
|
||||
case MsgRequest:
|
||||
return ShouldDiscardRequest(p, serverDuid)
|
||||
case MsgInformationRequest:
|
||||
return ShouldDiscardInformationRequest(p, serverDuid)
|
||||
default:
|
||||
return fmt.Errorf("Unknown packet")
|
||||
}
|
||||
}
|
||||
|
||||
func ShouldDiscardSolicit(p *Packet) error {
|
||||
options, _ := MakeOptions(p.Options)
|
||||
if !options.RequestedBootFileUrlOption() {
|
||||
return fmt.Errorf("'Solicit' packet doesn't have file url option")
|
||||
}
|
||||
if !options.HasClientId() {
|
||||
return fmt.Errorf("'Solicit' packet has no client id option")
|
||||
}
|
||||
if options.HasServerId() {
|
||||
return fmt.Errorf("'Solicit' packet has server id option")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ShouldDiscardRequest(p *Packet, serverDuid []byte) error {
|
||||
options, _ := MakeOptions(p.Options)
|
||||
if !options.RequestedBootFileUrlOption() {
|
||||
return fmt.Errorf("'Request' packet doesn't have file url option")
|
||||
}
|
||||
if !options.HasClientId() {
|
||||
return fmt.Errorf("'Request' packet has no client id option")
|
||||
}
|
||||
if !options.HasServerId() {
|
||||
return fmt.Errorf("'Request' packet has no server id option")
|
||||
}
|
||||
if bytes.Compare(options[OptServerId].Value, serverDuid) != 0 {
|
||||
return fmt.Errorf("'Request' packet's server id option (%d) is different from ours (%d)", options[OptServerId].Value, serverDuid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ShouldDiscardInformationRequest(p *Packet, serverDuid []byte) error {
|
||||
options, _ := MakeOptions(p.Options)
|
||||
if !options.RequestedBootFileUrlOption() {
|
||||
return fmt.Errorf("'Information-request' packet doesn't have boot file url option")
|
||||
}
|
||||
if options.HasIaNa() || options.HasIaTa() {
|
||||
return fmt.Errorf("'Information-request' packet has an IA option present")
|
||||
}
|
||||
if options.HasServerId() && (bytes.Compare(options[OptServerId].Value, serverDuid) != 0) {
|
||||
return fmt.Errorf("'Information-request' packet's server id option (%d) is different from ours (%d)", options[OptServerId].Value, serverDuid)
|
||||
}
|
||||
return nil
|
||||
}
|
BIN
dhcp6/testdata/dhcp6.pcap
vendored
Normal file
BIN
dhcp6/testdata/dhcp6.pcap
vendored
Normal file
Binary file not shown.
427
dhcp6/testdata/dhcp6.txt
vendored
Normal file
427
dhcp6/testdata/dhcp6.txt
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
No. Time Source Destination Protocol Length Info
|
||||
1 0.000000 fe80::2ad2:44ff:fe85:7b1d ff02::1:2 DHCPv6 161 Solicit XID: 0x958a89 CID: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
|
||||
Frame 1: 161 bytes on wire (1288 bits), 161 bytes captured (1288 bits)
|
||||
Encapsulation type: Ethernet (1)
|
||||
Arrival Time: Mar 31, 2017 13:57:48.861433000 BST
|
||||
[Time shift for this packet: 0.000000000 seconds]
|
||||
Epoch Time: 1490965068.861433000 seconds
|
||||
[Time delta from previous captured frame: 0.000000000 seconds]
|
||||
[Time delta from previous displayed frame: 0.000000000 seconds]
|
||||
[Time since reference or first frame: 0.000000000 seconds]
|
||||
Frame Number: 1
|
||||
Frame Length: 161 bytes (1288 bits)
|
||||
Capture Length: 161 bytes (1288 bits)
|
||||
[Frame is marked: False]
|
||||
[Frame is ignored: False]
|
||||
[Protocols in frame: eth:ethertype:ipv6:ipv6.nxt:udp:dhcpv6]
|
||||
[Coloring Rule Name: UDP]
|
||||
[Coloring Rule String: udp]
|
||||
Ethernet II, Src: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d), Dst: IPv6mcast_01:00:02 (33:33:00:01:00:02)
|
||||
Destination: IPv6mcast_01:00:02 (33:33:00:01:00:02)
|
||||
Address: IPv6mcast_01:00:02 (33:33:00:01:00:02)
|
||||
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
|
||||
.... ...1 .... .... .... .... = IG bit: Group address (multicast/broadcast)
|
||||
Source: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
Address: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
|
||||
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
|
||||
Type: IPv6 (0x86dd)
|
||||
Internet Protocol Version 6, Src: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d), Dst: ff02::1:2 (ff02::1:2)
|
||||
0110 .... = Version: 6
|
||||
[0110 .... = This field makes the filter "ip.version == 6" possible: 6]
|
||||
.... 0000 0000 .... .... .... .... .... = Traffic class: 0x00000000
|
||||
.... 0000 00.. .... .... .... .... .... = Differentiated Services Field: Default (0x00000000)
|
||||
.... .... ..0. .... .... .... .... .... = ECN-Capable Transport (ECT): Not set
|
||||
.... .... ...0 .... .... .... .... .... = ECN-CE: Not set
|
||||
.... .... .... 1100 1010 1001 0111 0111 = Flowlabel: 0x000ca977
|
||||
Payload length: 107
|
||||
Next header: UDP (17)
|
||||
Hop limit: 1
|
||||
Source: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d)
|
||||
[Source SA MAC: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)]
|
||||
Destination: ff02::1:2 (ff02::1:2)
|
||||
[Source GeoIP: Unknown]
|
||||
[Destination GeoIP: Unknown]
|
||||
User Datagram Protocol, Src Port: 546 (546), Dst Port: 547 (547)
|
||||
Source Port: 546 (546)
|
||||
Destination Port: 547 (547)
|
||||
Length: 107
|
||||
Checksum: 0xdae6 [validation disabled]
|
||||
[Good Checksum: False]
|
||||
[Bad Checksum: False]
|
||||
[Stream index: 0]
|
||||
DHCPv6
|
||||
Message type: Solicit (1)
|
||||
Transaction ID: 0x958a89
|
||||
Client Identifier
|
||||
Option: Client Identifier (1)
|
||||
Length: 18
|
||||
Value: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID Type: link-layer address (old) (4)
|
||||
Hardware type: Unknown (29892)
|
||||
Link-layer address: 75b0d742d4ca3681fe4bc6f66c24
|
||||
Option Request
|
||||
Option: Option Request (6)
|
||||
Length: 10
|
||||
Value: 00170018001700180001
|
||||
Requested Option code: DNS recursive name server (23)
|
||||
Requested Option code: Domain Search List (24)
|
||||
Requested Option code: DNS recursive name server (23)
|
||||
Requested Option code: Domain Search List (24)
|
||||
Requested Option code: Client Identifier (1)
|
||||
Elapsed time
|
||||
Option: Elapsed time (8)
|
||||
Length: 2
|
||||
Value: 0000
|
||||
Elapsed time: 0 ms
|
||||
Fully Qualified Domain Name
|
||||
Option: Fully Qualified Domain Name (39)
|
||||
Length: 33
|
||||
Value: 010e6c756369642d6e6f6e73656e73650c6170706c696564...
|
||||
0000 0... = Reserved: 0x00
|
||||
.... .0.. = N bit: Server should perform DNS updates
|
||||
.... ..0. = O bit: Server has not overridden client's S bit preference
|
||||
.... ...1 = S bit: Server should perform forward DNS updates
|
||||
Client FQDN: lucid-nonsense.appliedlogic.ca
|
||||
Identity Association for Non-temporary Address
|
||||
Option: Identity Association for Non-temporary Address (3)
|
||||
Length: 12
|
||||
Value: 44857b1d00000e1000001518
|
||||
IAID: 44857b1d
|
||||
T1: 3600
|
||||
T2: 5400
|
||||
|
||||
No. Time Source Destination Protocol Length Info
|
||||
2 0.000371 fe80::8a51:fbff:fe6b:d76d fe80::2ad2:44ff:fe85:7b1d DHCPv6 226 Advertise XID: 0x958a89 IAA: 2001:db8:1:1::99 CID: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
|
||||
Frame 2: 226 bytes on wire (1808 bits), 226 bytes captured (1808 bits)
|
||||
Encapsulation type: Ethernet (1)
|
||||
Arrival Time: Mar 31, 2017 13:57:48.861804000 BST
|
||||
[Time shift for this packet: 0.000000000 seconds]
|
||||
Epoch Time: 1490965068.861804000 seconds
|
||||
[Time delta from previous captured frame: 0.000371000 seconds]
|
||||
[Time delta from previous displayed frame: 0.000371000 seconds]
|
||||
[Time since reference or first frame: 0.000371000 seconds]
|
||||
Frame Number: 2
|
||||
Frame Length: 226 bytes (1808 bits)
|
||||
Capture Length: 226 bytes (1808 bits)
|
||||
[Frame is marked: False]
|
||||
[Frame is ignored: False]
|
||||
[Protocols in frame: eth:ethertype:ipv6:ipv6.nxt:udp:dhcpv6]
|
||||
[Coloring Rule Name: UDP]
|
||||
[Coloring Rule String: udp]
|
||||
Ethernet II, Src: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d), Dst: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
Destination: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
Address: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
|
||||
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
|
||||
Source: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d)
|
||||
Address: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d)
|
||||
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
|
||||
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
|
||||
Type: IPv6 (0x86dd)
|
||||
Internet Protocol Version 6, Src: fe80::8a51:fbff:fe6b:d76d (fe80::8a51:fbff:fe6b:d76d), Dst: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d)
|
||||
0110 .... = Version: 6
|
||||
[0110 .... = This field makes the filter "ip.version == 6" possible: 6]
|
||||
.... 0000 0000 .... .... .... .... .... = Traffic class: 0x00000000
|
||||
.... 0000 00.. .... .... .... .... .... = Differentiated Services Field: Default (0x00000000)
|
||||
.... .... ..0. .... .... .... .... .... = ECN-Capable Transport (ECT): Not set
|
||||
.... .... ...0 .... .... .... .... .... = ECN-CE: Not set
|
||||
.... .... .... 1001 0100 1000 0000 0001 = Flowlabel: 0x00094801
|
||||
Payload length: 172
|
||||
Next header: UDP (17)
|
||||
Hop limit: 64
|
||||
Source: fe80::8a51:fbff:fe6b:d76d (fe80::8a51:fbff:fe6b:d76d)
|
||||
[Source SA MAC: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d)]
|
||||
Destination: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d)
|
||||
[Destination SA MAC: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)]
|
||||
[Source GeoIP: Unknown]
|
||||
[Destination GeoIP: Unknown]
|
||||
User Datagram Protocol, Src Port: 547 (547), Dst Port: 546 (546)
|
||||
Source Port: 547 (547)
|
||||
Destination Port: 546 (546)
|
||||
Length: 172
|
||||
Checksum: 0x435f [validation disabled]
|
||||
[Good Checksum: False]
|
||||
[Bad Checksum: False]
|
||||
[Stream index: 1]
|
||||
DHCPv6
|
||||
Message type: Advertise (2)
|
||||
Transaction ID: 0x958a89
|
||||
Identity Association for Non-temporary Address
|
||||
Option: Identity Association for Non-temporary Address (3)
|
||||
Length: 40
|
||||
Value: 44857b1d00000e1000001c200005001820010db800010001...
|
||||
IAID: 44857b1d
|
||||
T1: 3600
|
||||
T2: 7200
|
||||
IA Address
|
||||
Option: IA Address (5)
|
||||
Length: 24
|
||||
Value: 20010db800010001000000000000009900093a8000278d00
|
||||
IPv6 address: 2001:db8:1:1::99 (2001:db8:1:1::99)
|
||||
Preferred lifetime: 604800
|
||||
Valid lifetime: 2592000
|
||||
Client Identifier
|
||||
Option: Client Identifier (1)
|
||||
Length: 18
|
||||
Value: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID Type: link-layer address (old) (4)
|
||||
Hardware type: Unknown (29892)
|
||||
Link-layer address: 75b0d742d4ca3681fe4bc6f66c24
|
||||
Server Identifier
|
||||
Option: Server Identifier (2)
|
||||
Length: 14
|
||||
Value: 00010001207106ee8851fb6bd76d
|
||||
DUID: 00010001207106ee8851fb6bd76d
|
||||
DUID Type: link-layer address plus time (1)
|
||||
Hardware type: Ethernet (1)
|
||||
DUID Time: Mar 31, 2017 13:24:14.000000000 BST
|
||||
Link-layer address: 88:51:fb:6b:d7:6d
|
||||
DNS recursive name server
|
||||
Option: DNS recursive name server (23)
|
||||
Length: 16
|
||||
Value: 20010db8000100010000000000000201
|
||||
1 DNS server address: 2001:db8:1:1::201 (2001:db8:1:1::201)
|
||||
Domain Search List
|
||||
Option: Domain Search List (24)
|
||||
Length: 14
|
||||
Value: 08696e7465726e616c036c616e00
|
||||
DNS Domain Search List
|
||||
Domain Search List FQDN: internal.lan
|
||||
DNS recursive name server
|
||||
Option: DNS recursive name server (23)
|
||||
Length: 16
|
||||
Value: 20010db8000100010000000000000201
|
||||
1 DNS server address: 2001:db8:1:1::201 (2001:db8:1:1::201)
|
||||
Domain Search List
|
||||
Option: Domain Search List (24)
|
||||
Length: 14
|
||||
Value: 08696e7465726e616c036c616e00
|
||||
DNS Domain Search List
|
||||
Domain Search List FQDN: internal.lan
|
||||
|
||||
No. Time Source Destination Protocol Length Info
|
||||
3 1.061280 fe80::2ad2:44ff:fe85:7b1d ff02::1:2 DHCPv6 207 Request XID: 0xfa052 CID: 000474c475b0d742d4ca3681fe4bc6f66c24 IAA: 2001:db8:1:1::99
|
||||
|
||||
Frame 3: 207 bytes on wire (1656 bits), 207 bytes captured (1656 bits)
|
||||
Encapsulation type: Ethernet (1)
|
||||
Arrival Time: Mar 31, 2017 13:57:49.922713000 BST
|
||||
[Time shift for this packet: 0.000000000 seconds]
|
||||
Epoch Time: 1490965069.922713000 seconds
|
||||
[Time delta from previous captured frame: 1.060909000 seconds]
|
||||
[Time delta from previous displayed frame: 1.060909000 seconds]
|
||||
[Time since reference or first frame: 1.061280000 seconds]
|
||||
Frame Number: 3
|
||||
Frame Length: 207 bytes (1656 bits)
|
||||
Capture Length: 207 bytes (1656 bits)
|
||||
[Frame is marked: False]
|
||||
[Frame is ignored: False]
|
||||
[Protocols in frame: eth:ethertype:ipv6:ipv6.nxt:udp:dhcpv6]
|
||||
[Coloring Rule Name: UDP]
|
||||
[Coloring Rule String: udp]
|
||||
Ethernet II, Src: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d), Dst: IPv6mcast_01:00:02 (33:33:00:01:00:02)
|
||||
Destination: IPv6mcast_01:00:02 (33:33:00:01:00:02)
|
||||
Address: IPv6mcast_01:00:02 (33:33:00:01:00:02)
|
||||
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
|
||||
.... ...1 .... .... .... .... = IG bit: Group address (multicast/broadcast)
|
||||
Source: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
Address: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
|
||||
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
|
||||
Type: IPv6 (0x86dd)
|
||||
Internet Protocol Version 6, Src: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d), Dst: ff02::1:2 (ff02::1:2)
|
||||
0110 .... = Version: 6
|
||||
[0110 .... = This field makes the filter "ip.version == 6" possible: 6]
|
||||
.... 0000 0000 .... .... .... .... .... = Traffic class: 0x00000000
|
||||
.... 0000 00.. .... .... .... .... .... = Differentiated Services Field: Default (0x00000000)
|
||||
.... .... ..0. .... .... .... .... .... = ECN-Capable Transport (ECT): Not set
|
||||
.... .... ...0 .... .... .... .... .... = ECN-CE: Not set
|
||||
.... .... .... 1100 1010 1001 0111 0111 = Flowlabel: 0x000ca977
|
||||
Payload length: 153
|
||||
Next header: UDP (17)
|
||||
Hop limit: 1
|
||||
Source: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d)
|
||||
[Source SA MAC: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)]
|
||||
Destination: ff02::1:2 (ff02::1:2)
|
||||
[Source GeoIP: Unknown]
|
||||
[Destination GeoIP: Unknown]
|
||||
User Datagram Protocol, Src Port: 546 (546), Dst Port: 547 (547)
|
||||
Source Port: 546 (546)
|
||||
Destination Port: 547 (547)
|
||||
Length: 153
|
||||
Checksum: 0x4743 [validation disabled]
|
||||
[Good Checksum: False]
|
||||
[Bad Checksum: False]
|
||||
[Stream index: 0]
|
||||
DHCPv6
|
||||
Message type: Request (3)
|
||||
Transaction ID: 0x0fa052
|
||||
Client Identifier
|
||||
Option: Client Identifier (1)
|
||||
Length: 18
|
||||
Value: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID Type: link-layer address (old) (4)
|
||||
Hardware type: Unknown (29892)
|
||||
Link-layer address: 75b0d742d4ca3681fe4bc6f66c24
|
||||
Server Identifier
|
||||
Option: Server Identifier (2)
|
||||
Length: 14
|
||||
Value: 00010001207106ee8851fb6bd76d
|
||||
DUID: 00010001207106ee8851fb6bd76d
|
||||
DUID Type: link-layer address plus time (1)
|
||||
Hardware type: Ethernet (1)
|
||||
DUID Time: Mar 31, 2017 13:24:14.000000000 BST
|
||||
Link-layer address: 88:51:fb:6b:d7:6d
|
||||
Option Request
|
||||
Option: Option Request (6)
|
||||
Length: 10
|
||||
Value: 00170018001700180001
|
||||
Requested Option code: DNS recursive name server (23)
|
||||
Requested Option code: Domain Search List (24)
|
||||
Requested Option code: DNS recursive name server (23)
|
||||
Requested Option code: Domain Search List (24)
|
||||
Requested Option code: Client Identifier (1)
|
||||
Elapsed time
|
||||
Option: Elapsed time (8)
|
||||
Length: 2
|
||||
Value: 0000
|
||||
Elapsed time: 0 ms
|
||||
Fully Qualified Domain Name
|
||||
Option: Fully Qualified Domain Name (39)
|
||||
Length: 33
|
||||
Value: 010e6c756369642d6e6f6e73656e73650c6170706c696564...
|
||||
0000 0... = Reserved: 0x00
|
||||
.... .0.. = N bit: Server should perform DNS updates
|
||||
.... ..0. = O bit: Server has not overridden client's S bit preference
|
||||
.... ...1 = S bit: Server should perform forward DNS updates
|
||||
Client FQDN: lucid-nonsense.appliedlogic.ca
|
||||
Identity Association for Non-temporary Address
|
||||
Option: Identity Association for Non-temporary Address (3)
|
||||
Length: 40
|
||||
Value: 44857b1d00000e10000015180005001820010db800010001...
|
||||
IAID: 44857b1d
|
||||
T1: 3600
|
||||
T2: 5400
|
||||
IA Address
|
||||
Option: IA Address (5)
|
||||
Length: 24
|
||||
Value: 20010db800010001000000000000009900001c2000001d4c
|
||||
IPv6 address: 2001:db8:1:1::99 (2001:db8:1:1::99)
|
||||
Preferred lifetime: 7200
|
||||
Valid lifetime: 7500
|
||||
|
||||
No. Time Source Destination Protocol Length Info
|
||||
4 1.061709 fe80::8a51:fbff:fe6b:d76d fe80::2ad2:44ff:fe85:7b1d DHCPv6 226 Reply XID: 0xfa052 IAA: 2001:db8:1:1::99 CID: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
|
||||
Frame 4: 226 bytes on wire (1808 bits), 226 bytes captured (1808 bits)
|
||||
Encapsulation type: Ethernet (1)
|
||||
Arrival Time: Mar 31, 2017 13:57:49.923142000 BST
|
||||
[Time shift for this packet: 0.000000000 seconds]
|
||||
Epoch Time: 1490965069.923142000 seconds
|
||||
[Time delta from previous captured frame: 0.000429000 seconds]
|
||||
[Time delta from previous displayed frame: 0.000429000 seconds]
|
||||
[Time since reference or first frame: 1.061709000 seconds]
|
||||
Frame Number: 4
|
||||
Frame Length: 226 bytes (1808 bits)
|
||||
Capture Length: 226 bytes (1808 bits)
|
||||
[Frame is marked: False]
|
||||
[Frame is ignored: False]
|
||||
[Protocols in frame: eth:ethertype:ipv6:ipv6.nxt:udp:dhcpv6]
|
||||
[Coloring Rule Name: UDP]
|
||||
[Coloring Rule String: udp]
|
||||
Ethernet II, Src: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d), Dst: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
Destination: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
Address: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)
|
||||
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
|
||||
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
|
||||
Source: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d)
|
||||
Address: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d)
|
||||
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
|
||||
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
|
||||
Type: IPv6 (0x86dd)
|
||||
Internet Protocol Version 6, Src: fe80::8a51:fbff:fe6b:d76d (fe80::8a51:fbff:fe6b:d76d), Dst: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d)
|
||||
0110 .... = Version: 6
|
||||
[0110 .... = This field makes the filter "ip.version == 6" possible: 6]
|
||||
.... 0000 0000 .... .... .... .... .... = Traffic class: 0x00000000
|
||||
.... 0000 00.. .... .... .... .... .... = Differentiated Services Field: Default (0x00000000)
|
||||
.... .... ..0. .... .... .... .... .... = ECN-Capable Transport (ECT): Not set
|
||||
.... .... ...0 .... .... .... .... .... = ECN-CE: Not set
|
||||
.... .... .... 1001 0100 1000 0000 0001 = Flowlabel: 0x00094801
|
||||
Payload length: 172
|
||||
Next header: UDP (17)
|
||||
Hop limit: 64
|
||||
Source: fe80::8a51:fbff:fe6b:d76d (fe80::8a51:fbff:fe6b:d76d)
|
||||
[Source SA MAC: HewlettP_6b:d7:6d (88:51:fb:6b:d7:6d)]
|
||||
Destination: fe80::2ad2:44ff:fe85:7b1d (fe80::2ad2:44ff:fe85:7b1d)
|
||||
[Destination SA MAC: LcfcHefe_85:7b:1d (28:d2:44:85:7b:1d)]
|
||||
[Source GeoIP: Unknown]
|
||||
[Destination GeoIP: Unknown]
|
||||
User Datagram Protocol, Src Port: 547 (547), Dst Port: 546 (546)
|
||||
Source Port: 547 (547)
|
||||
Destination Port: 546 (546)
|
||||
Length: 172
|
||||
Checksum: 0x435f [validation disabled]
|
||||
[Good Checksum: False]
|
||||
[Bad Checksum: False]
|
||||
[Stream index: 1]
|
||||
DHCPv6
|
||||
Message type: Reply (7)
|
||||
Transaction ID: 0x0fa052
|
||||
Identity Association for Non-temporary Address
|
||||
Option: Identity Association for Non-temporary Address (3)
|
||||
Length: 40
|
||||
Value: 44857b1d00000e1000001c200005001820010db800010001...
|
||||
IAID: 44857b1d
|
||||
T1: 3600
|
||||
T2: 7200
|
||||
IA Address
|
||||
Option: IA Address (5)
|
||||
Length: 24
|
||||
Value: 20010db800010001000000000000009900093a8000278d00
|
||||
IPv6 address: 2001:db8:1:1::99 (2001:db8:1:1::99)
|
||||
Preferred lifetime: 604800
|
||||
Valid lifetime: 2592000
|
||||
Client Identifier
|
||||
Option: Client Identifier (1)
|
||||
Length: 18
|
||||
Value: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID: 000474c475b0d742d4ca3681fe4bc6f66c24
|
||||
DUID Type: link-layer address (old) (4)
|
||||
Hardware type: Unknown (29892)
|
||||
Link-layer address: 75b0d742d4ca3681fe4bc6f66c24
|
||||
Server Identifier
|
||||
Option: Server Identifier (2)
|
||||
Length: 14
|
||||
Value: 00010001207106ee8851fb6bd76d
|
||||
DUID: 00010001207106ee8851fb6bd76d
|
||||
DUID Type: link-layer address plus time (1)
|
||||
Hardware type: Ethernet (1)
|
||||
DUID Time: Mar 31, 2017 13:24:14.000000000 BST
|
||||
Link-layer address: 88:51:fb:6b:d7:6d
|
||||
DNS recursive name server
|
||||
Option: DNS recursive name server (23)
|
||||
Length: 16
|
||||
Value: 20010db8000100010000000000000201
|
||||
1 DNS server address: 2001:db8:1:1::201 (2001:db8:1:1::201)
|
||||
Domain Search List
|
||||
Option: Domain Search List (24)
|
||||
Length: 14
|
||||
Value: 08696e7465726e616c036c616e00
|
||||
DNS Domain Search List
|
||||
Domain Search List FQDN: internal.lan
|
||||
DNS recursive name server
|
||||
Option: DNS recursive name server (23)
|
||||
Length: 16
|
||||
Value: 20010db8000100010000000000000201
|
||||
1 DNS server address: 2001:db8:1:1::201 (2001:db8:1:1::201)
|
||||
Domain Search List
|
||||
Option: Domain Search List (24)
|
||||
Length: 14
|
||||
Value: 08696e7465726e616c036c616e00
|
||||
DNS Domain Search List
|
||||
Domain Search List FQDN: internal.lan
|
34
pixiecore/dhcpv6.go
Normal file
34
pixiecore/dhcpv6.go
Normal file
@ -0,0 +1,34 @@
|
||||
package pixiecore
|
||||
|
||||
import (
|
||||
"go.universe.tf/netboot/dhcp6"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (s *ServerV6) serveDHCP(conn *dhcp6.Conn) error {
|
||||
s.log("dhcpv6", "Waiting for packets...\n")
|
||||
for {
|
||||
pkt, src, err := conn.RecvDHCP()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error receiving DHCP packet: %s", err)
|
||||
}
|
||||
if err := pkt.ShouldDiscard(s.Duid); err != nil {
|
||||
s.log("dhcpv6", fmt.Sprintf("Discarding (%d) packet (%d): %s\n", pkt.Type, pkt.TransactionID, err))
|
||||
continue
|
||||
}
|
||||
|
||||
opts, _ := pkt.UnmarshalOptions()
|
||||
s.log("dhcpv6", fmt.Sprintf("Received (%d) packet (%d): %s\n", pkt.Type, pkt.TransactionID, opts.HumanReadable()))
|
||||
|
||||
response, _ := pkt.BuildResponse(s.Duid)
|
||||
if err := conn.SendDHCP(src, response); err != nil {
|
||||
s.log("dhcpv6", fmt.Sprintf("Error sending reply (%d) (%d): %s", pkt.Type, pkt.TransactionID, err))
|
||||
continue
|
||||
}
|
||||
|
||||
reply_packet := dhcp6.MakePacket(response, len(response))
|
||||
reply_opts,_ := reply_packet.UnmarshalOptions()
|
||||
|
||||
s.log("dhcpv6", fmt.Sprintf("Sent (%d) packet (%d): %s\n", reply_packet.Type, reply_packet.TransactionID, reply_opts.HumanReadable()))
|
||||
}
|
||||
}
|
91
pixiecore/pixicorev6.go
Normal file
91
pixiecore/pixicorev6.go
Normal file
@ -0,0 +1,91 @@
|
||||
package pixiecore
|
||||
|
||||
import (
|
||||
"go.universe.tf/netboot/dhcp6"
|
||||
"sync"
|
||||
"fmt"
|
||||
"time"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
)
|
||||
|
||||
type ServerV6 struct {
|
||||
Address string
|
||||
Port string
|
||||
Duid []byte
|
||||
|
||||
errs chan error
|
||||
|
||||
eventsMu sync.Mutex
|
||||
events map[string][]machineEvent
|
||||
|
||||
Log func(subsystem, msg string)
|
||||
Debug func(subsystem, msg string)
|
||||
}
|
||||
|
||||
func (s *ServerV6) Serve() error {
|
||||
s.log("dhcp", "starting...")
|
||||
|
||||
dhcp, err := dhcp6.NewConn(s.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.log("dhcp", "new connection...")
|
||||
|
||||
s.events = make(map[string][]machineEvent)
|
||||
// 5 buffer slots, one for each goroutine, plus one for
|
||||
// Shutdown(). We only ever pull the first error out, but shutdown
|
||||
// will likely generate some spurious errors from the other
|
||||
// goroutines, and we want them to be able to dump them without
|
||||
// blocking.
|
||||
s.errs = make(chan error, 6)
|
||||
|
||||
//s.debug("Init", "Starting Pixiecore goroutines")
|
||||
s.SetDUID(dhcp.SourceHardwareAddress())
|
||||
go func() { s.errs <- s.serveDHCP(dhcp) }()
|
||||
|
||||
// Wait for either a fatal error, or Shutdown().
|
||||
err = <-s.errs
|
||||
dhcp.Close()
|
||||
|
||||
s.log("dhcp", "stopped...")
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown causes Serve() to exit, cleaning up behind itself.
|
||||
func (s *ServerV6) Shutdown() {
|
||||
select {
|
||||
case s.errs <- nil:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerV6) log(subsystem, format string, args ...interface{}) {
|
||||
if s.Log == nil {
|
||||
return
|
||||
}
|
||||
s.Log(subsystem, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (s *ServerV6) debug(subsystem, format string, args ...interface{}) {
|
||||
if s.Debug == nil {
|
||||
return
|
||||
}
|
||||
s.Debug(subsystem, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (s *ServerV6) SetDUID(addr net.HardwareAddr) {
|
||||
duid := make([]byte, len(addr) + 8) // see rfc3315, section 9.2, DUID-LT
|
||||
|
||||
copy(duid[0:], []byte{0, 1}) //fixed, x0001
|
||||
copy(duid[2:], []byte{0, 1}) //hw type ethernet, x0001
|
||||
|
||||
utcLoc, _ := time.LoadLocation("UTC")
|
||||
sinceJanFirst2000 := time.Since(time.Date(2000, time.January, 1, 0, 0, 0, 0, utcLoc))
|
||||
binary.BigEndian.PutUint32(duid[4:], uint32(sinceJanFirst2000.Seconds()))
|
||||
|
||||
copy(duid[8:], addr)
|
||||
|
||||
s.Duid = duid
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user