probe/internal/protocol/dhcp/dhcp.go
2018-08-29 17:21:08 +09:00

143 lines
3.9 KiB
Go

package dhcp
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"strings"
"time"
"github.com/google/gopacket/layers"
"github.com/google/gopacket"
gl "github.com/google/gopacket/layers"
)
func doDiscoverV4(address string) (map[string]string, error) {
localAddr, err := net.ResolveUDPAddr("udp", address+":68")
if err != nil {
return nil, err
}
conn, err := net.ListenUDP("udp", localAddr)
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(time.Second * 3))
defer func() {
conn.Close()
}()
sendDiscover(conn) // broadcast 255.255.255.255:67
return readOffer(conn)
}
func sendDiscover(conn *net.UDPConn) error {
server := fmt.Sprintf("%s:%d", "255.255.255.255", 67)
serverAddr, err := net.ResolveUDPAddr("udp", server)
if err != nil {
return err
}
dhcp := &gl.DHCPv4{Operation: gl.DHCPOpRequest, HardwareType: gl.LinkTypeEthernet, Xid: 0x12345678,
ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{0, 0, 0, 0}, NextServerIP: net.IP{0, 0, 0, 0}, RelayAgentIP: net.IP{0, 0, 0, 0},
ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
ServerName: make([]byte, 64), File: make([]byte, 128)}
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptMessageType, []byte{byte(gl.DHCPMsgTypeDiscover)}))
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptHostname, []byte{'o', 'v', 'e', 'r', 'f', 'l', 'o', 'w', '.', 'c', 'o', 'm'}))
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptPad, nil))
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptParamsRequest,
[]byte{byte(gl.DHCPOptSubnetMask), byte(gl.DHCPOptBroadcastAddr), byte(gl.DHCPOptTimeOffset),
byte(gl.DHCPOptRouter), byte(gl.DHCPOptDomainName), byte(gl.DHCPOptDNS), byte(gl.DHCPOptDomainSearch),
byte(gl.DHCPOptHostname), byte(gl.DHCPOptNetBIOSTCPNS), byte(gl.DHCPOptInterfaceMTU), byte(gl.DHCPOptClasslessStaticRoute),
byte(gl.DHCPOptNTPServers)}))
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{FixLengths: true}
err = gopacket.SerializeLayers(buf, opts, dhcp)
if err != nil {
return err
}
_, err = conn.WriteToUDP(buf.Bytes(), serverAddr)
if err != nil {
return err
}
return nil
}
func readOffer(conn *net.UDPConn) (map[string]string, error) {
offerOptions := make(map[string]string)
buf := make([]byte, 1024)
n, _, err := conn.ReadFromUDP(buf)
if err != nil {
return nil, err
}
if n <= 0 {
return nil, fmt.Errorf("%s", "Emptry response.")
}
var dhcp gl.DHCPv4
decoded := []gopacket.LayerType{}
parser := gopacket.NewDecodingLayerParser(gl.LayerTypeDHCPv4, &dhcp)
if err = parser.DecodeLayers(buf, &decoded); err != nil {
return nil, err
}
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeDHCPv4:
for _, opt := range dhcp.Options {
switch opt.Type {
case gl.DHCPOptServerID:
offerOptions["DHCP_SERVER"] = optDataToIPString(opt.Data)
break
case gl.DHCPOptLeaseTime:
var sec int32
if err = binary.Read(bytes.NewReader(opt.Data), binary.BigEndian, &sec); err == nil {
offerOptions["LEASE_TIME"] = fmt.Sprintf("%d", sec)
}
break
case gl.DHCPOptSubnetMask:
offerOptions["SUBNET_MASK"] = optDataToIPString(opt.Data)
break
case gl.DHCPOptRouter:
offerOptions["ROUTER"] = optDataToIPString(opt.Data)
break
case gl.DHCPOptDNS:
cnt := len(opt.Data) / 4
var dns []string
ipStr := optDataToIPString(opt.Data[0:4])
dns = append(dns, ipStr)
if cnt > 1 {
ipStr := optDataToIPString(opt.Data[4:8])
dns = append(dns, ipStr)
}
offerOptions["DOMAIN_NAME_SERVER"] = strings.Join(dns, ",")
break
}
}
}
}
return offerOptions, nil
}
func optDataToIPString(b []byte) string {
var ipStr string
len := len(b)
for i := 0; i < len; i++ {
v := b[i]
ipStr += fmt.Sprintf("%d", v)
if i < len-1 {
ipStr += "."
}
}
return ipStr
}