Commit f19a51db authored by Sander van Harmelen's avatar Sander van Harmelen

godep: add dhcp4 and dhcp4client dependencies

parent 04899f23
...@@ -826,6 +826,14 @@ ...@@ -826,6 +826,14 @@
"Rev": "71acacd42f85e5e82f70a55327789582a5200a90" "Rev": "71acacd42f85e5e82f70a55327789582a5200a90"
}, },
{ {
"ImportPath": "github.com/d2g/dhcp4",
"Rev": "a1d1b6c41b1ce8a71a5121a9cee31809c4707d9c"
},
{
"ImportPath": "github.com/d2g/dhcp4client",
"Rev": "6e570ed0a266b730a860ba1068090f683b2c213a"
},
{
"ImportPath": "github.com/davecgh/go-spew/spew", "ImportPath": "github.com/davecgh/go-spew/spew",
"Comment": "v1.1.0-1-g782f496", "Comment": "v1.1.0-1-g782f496",
"Rev": "782f4967f2dc4564575ca782fe2d04090b5faca8" "Rev": "782f4967f2dc4564575ca782fe2d04090b5faca8"
......
...@@ -10,12 +10,23 @@ go_library( ...@@ -10,12 +10,23 @@ go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"cloudstack.go", "cloudstack.go",
"cloudstack_instances.go",
"cloudstack_loadbalancer.go", "cloudstack_loadbalancer.go",
], "metadata.go",
"metadata_other.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"metadata_linux.go",
],
"//conditions:default": [],
}),
deps = [ deps = [
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//vendor/github.com/d2g/dhcp4:go_default_library",
"//vendor/github.com/d2g/dhcp4client:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/kardianos/osext:go_default_library",
"//vendor/github.com/xanzy/go-cloudstack/cloudstack:go_default_library", "//vendor/github.com/xanzy/go-cloudstack/cloudstack:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library", "//vendor/gopkg.in/gcfg.v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
......
...@@ -119,6 +119,8 @@ filegroup( ...@@ -119,6 +119,8 @@ filegroup(
"//vendor/github.com/coreos/pkg/timeutil:all-srcs", "//vendor/github.com/coreos/pkg/timeutil:all-srcs",
"//vendor/github.com/coreos/rkt/api/v1alpha:all-srcs", "//vendor/github.com/coreos/rkt/api/v1alpha:all-srcs",
"//vendor/github.com/cpuguy83/go-md2man/md2man:all-srcs", "//vendor/github.com/cpuguy83/go-md2man/md2man:all-srcs",
"//vendor/github.com/d2g/dhcp4:all-srcs",
"//vendor/github.com/d2g/dhcp4client:all-srcs",
"//vendor/github.com/davecgh/go-spew/spew:all-srcs", "//vendor/github.com/davecgh/go-spew/spew:all-srcs",
"//vendor/github.com/daviddengcn/go-colortext:all-srcs", "//vendor/github.com/daviddengcn/go-colortext:all-srcs",
"//vendor/github.com/dgrijalva/jwt-go:all-srcs", "//vendor/github.com/dgrijalva/jwt-go:all-srcs",
......
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"constants.go",
"helpers.go",
"option.go",
"packet.go",
],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
Copyright (c) 2013 Skagerrak Software Limited. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Skagerrak Software Limited nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
# DHCP4 - A DHCP library written in Go.
Warning: This library is still being developed. Function calls will change.
I've removed Server Functionality, for me this project supports the underlying DHCP format not the implementation.
package dhcp4
// OpCodes
const (
BootRequest OpCode = 1 // From Client
BootReply OpCode = 2 // From Server
)
// DHCP Message Type 53
const (
Discover MessageType = 1 // Broadcast Packet From Client - Can I have an IP?
Offer MessageType = 2 // Broadcast From Server - Here's an IP
Request MessageType = 3 // Broadcast From Client - I'll take that IP (Also start for renewals)
Decline MessageType = 4 // Broadcast From Client - Sorry I can't use that IP
ACK MessageType = 5 // From Server, Yes you can have that IP
NAK MessageType = 6 // From Server, No you cannot have that IP
Release MessageType = 7 // From Client, I don't need that IP anymore
Inform MessageType = 8 // From Client, I have this IP and there's nothing you can do about it
)
// DHCP Options
const (
End OptionCode = 255
Pad OptionCode = 0
OptionSubnetMask OptionCode = 1
OptionTimeOffset OptionCode = 2
OptionRouter OptionCode = 3
OptionTimeServer OptionCode = 4
OptionNameServer OptionCode = 5
OptionDomainNameServer OptionCode = 6
OptionLogServer OptionCode = 7
OptionCookieServer OptionCode = 8
OptionLPRServer OptionCode = 9
OptionImpressServer OptionCode = 10
OptionResourceLocationServer OptionCode = 11
OptionHostName OptionCode = 12
OptionBootFileSize OptionCode = 13
OptionMeritDumpFile OptionCode = 14
OptionDomainName OptionCode = 15
OptionSwapServer OptionCode = 16
OptionRootPath OptionCode = 17
OptionExtensionsPath OptionCode = 18
// IP Layer Parameters per Host
OptionIPForwardingEnableDisable OptionCode = 19
OptionNonLocalSourceRoutingEnableDisable OptionCode = 20
OptionPolicyFilter OptionCode = 21
OptionMaximumDatagramReassemblySize OptionCode = 22
OptionDefaultIPTimeToLive OptionCode = 23
OptionPathMTUAgingTimeout OptionCode = 24
OptionPathMTUPlateauTable OptionCode = 25
// IP Layer Parameters per Interface
OptionInterfaceMTU OptionCode = 26
OptionAllSubnetsAreLocal OptionCode = 27
OptionBroadcastAddress OptionCode = 28
OptionPerformMaskDiscovery OptionCode = 29
OptionMaskSupplier OptionCode = 30
OptionPerformRouterDiscovery OptionCode = 31
OptionRouterSolicitationAddress OptionCode = 32
OptionStaticRoute OptionCode = 33
// Link Layer Parameters per Interface
OptionTrailerEncapsulation OptionCode = 34
OptionARPCacheTimeout OptionCode = 35
OptionEthernetEncapsulation OptionCode = 36
// TCP Parameters
OptionTCPDefaultTTL OptionCode = 37
OptionTCPKeepaliveInterval OptionCode = 38
OptionTCPKeepaliveGarbage OptionCode = 39
// Application and Service Parameters
OptionNetworkInformationServiceDomain OptionCode = 40
OptionNetworkInformationServers OptionCode = 41
OptionNetworkTimeProtocolServers OptionCode = 42
OptionVendorSpecificInformation OptionCode = 43
OptionNetBIOSOverTCPIPNameServer OptionCode = 44
OptionNetBIOSOverTCPIPDatagramDistributionServer OptionCode = 45
OptionNetBIOSOverTCPIPNodeType OptionCode = 46
OptionNetBIOSOverTCPIPScope OptionCode = 47
OptionXWindowSystemFontServer OptionCode = 48
OptionXWindowSystemDisplayManager OptionCode = 49
OptionNetworkInformationServicePlusDomain OptionCode = 64
OptionNetworkInformationServicePlusServers OptionCode = 65
OptionMobileIPHomeAgent OptionCode = 68
OptionSimpleMailTransportProtocol OptionCode = 69
OptionPostOfficeProtocolServer OptionCode = 70
OptionNetworkNewsTransportProtocol OptionCode = 71
OptionDefaultWorldWideWebServer OptionCode = 72
OptionDefaultFingerServer OptionCode = 73
OptionDefaultInternetRelayChatServer OptionCode = 74
OptionStreetTalkServer OptionCode = 75
OptionStreetTalkDirectoryAssistance OptionCode = 76
// DHCP Extensions
OptionRequestedIPAddress OptionCode = 50
OptionIPAddressLeaseTime OptionCode = 51
OptionOverload OptionCode = 52
OptionDHCPMessageType OptionCode = 53
OptionServerIdentifier OptionCode = 54
OptionParameterRequestList OptionCode = 55
OptionMessage OptionCode = 56
OptionMaximumDHCPMessageSize OptionCode = 57
OptionRenewalTimeValue OptionCode = 58
OptionRebindingTimeValue OptionCode = 59
OptionVendorClassIdentifier OptionCode = 60
OptionClientIdentifier OptionCode = 61
OptionTFTPServerName OptionCode = 66
OptionBootFileName OptionCode = 67
OptionUserClass OptionCode = 77
OptionClientArchitecture OptionCode = 93
OptionTZPOSIXString OptionCode = 100
OptionTZDatabaseString OptionCode = 101
OptionClasslessRouteFormat OptionCode = 121
)
package dhcp4
import (
"encoding/binary"
"net"
"time"
)
// IPRange returns how many ips in the ip range from start to stop (inclusive)
func IPRange(start, stop net.IP) int {
//return int(Uint([]byte(stop))-Uint([]byte(start))) + 1
return int(binary.BigEndian.Uint32(stop.To4())) - int(binary.BigEndian.Uint32(start.To4())) + 1
}
// IPAdd returns a copy of start + add.
// IPAdd(net.IP{192,168,1,1},30) returns net.IP{192.168.1.31}
func IPAdd(start net.IP, add int) net.IP { // IPv4 only
start = start.To4()
//v := Uvarint([]byte(start))
result := make(net.IP, 4)
binary.BigEndian.PutUint32(result, binary.BigEndian.Uint32(start)+uint32(add))
//PutUint([]byte(result), v+uint64(add))
return result
}
// IPLess returns where IP a is less than IP b.
func IPLess(a, b net.IP) bool {
b = b.To4()
for i, ai := range a.To4() {
if ai != b[i] {
return ai < b[i]
}
}
return false
}
// IPInRange returns true if ip is between (inclusive) start and stop.
func IPInRange(start, stop, ip net.IP) bool {
return !(IPLess(ip, start) || IPLess(stop, ip))
}
// OptionsLeaseTime - converts a time.Duration to a 4 byte slice, compatible
// with OptionIPAddressLeaseTime.
func OptionsLeaseTime(d time.Duration) []byte {
leaseBytes := make([]byte, 4)
binary.BigEndian.PutUint32(leaseBytes, uint32(d/time.Second))
//PutUvarint(leaseBytes, uint64(d/time.Second))
return leaseBytes
}
// JoinIPs returns a byte slice of IP addresses, one immediately after the other
// This may be useful for creating multiple IP options such as OptionRouter.
func JoinIPs(ips []net.IP) (b []byte) {
for _, v := range ips {
b = append(b, v.To4()...)
}
return
}
package dhcp4
type OptionCode byte
type Option struct {
Code OptionCode
Value []byte
}
// Map of DHCP options
type Options map[OptionCode][]byte
// SelectOrderOrAll has same functionality as SelectOrder, except if the order
// param is nil, whereby all options are added (in arbitrary order).
func (o Options) SelectOrderOrAll(order []byte) []Option {
if order == nil {
opts := make([]Option, 0, len(o))
for i, v := range o {
opts = append(opts, Option{Code: i, Value: v})
}
return opts
}
return o.SelectOrder(order)
}
// SelectOrder returns a slice of options ordered and selected by a byte array
// usually defined by OptionParameterRequestList. This result is expected to be
// used in ReplyPacket()'s []Option parameter.
func (o Options) SelectOrder(order []byte) []Option {
opts := make([]Option, 0, len(order))
for _, v := range order {
if data, ok := o[OptionCode(v)]; ok {
opts = append(opts, Option{Code: OptionCode(v), Value: data})
}
}
return opts
}
type OpCode byte
type MessageType byte // Option 53
package dhcp4
import (
"net"
"time"
)
// A DHCP packet
type Packet []byte
func (p Packet) OpCode() OpCode { return OpCode(p[0]) }
func (p Packet) HType() byte { return p[1] }
func (p Packet) HLen() byte { return p[2] }
func (p Packet) Hops() byte { return p[3] }
func (p Packet) XId() []byte { return p[4:8] }
func (p Packet) Secs() []byte { return p[8:10] } // Never Used?
func (p Packet) Flags() []byte { return p[10:12] }
func (p Packet) CIAddr() net.IP { return net.IP(p[12:16]) }
func (p Packet) YIAddr() net.IP { return net.IP(p[16:20]) }
func (p Packet) SIAddr() net.IP { return net.IP(p[20:24]) }
func (p Packet) GIAddr() net.IP { return net.IP(p[24:28]) }
func (p Packet) CHAddr() net.HardwareAddr {
hLen := p.HLen()
if hLen > 16 { // Prevent chaddr exceeding p boundary
hLen = 16
}
return net.HardwareAddr(p[28 : 28+hLen]) // max endPos 44
}
// 192 bytes of zeros BOOTP legacy
func (p Packet) Cookie() []byte { return p[236:240] }
func (p Packet) Options() []byte {
if len(p) > 240 {
return p[240:]
}
return nil
}
func (p Packet) Broadcast() bool { return p.Flags()[0] > 127 }
func (p Packet) SetBroadcast(broadcast bool) {
if p.Broadcast() != broadcast {
p.Flags()[0] ^= 128
}
}
func (p Packet) SetOpCode(c OpCode) { p[0] = byte(c) }
func (p Packet) SetCHAddr(a net.HardwareAddr) {
copy(p[28:44], a)
p[2] = byte(len(a))
}
func (p Packet) SetHType(hType byte) { p[1] = hType }
func (p Packet) SetCookie(cookie []byte) { copy(p.Cookie(), cookie) }
func (p Packet) SetHops(hops byte) { p[3] = hops }
func (p Packet) SetXId(xId []byte) { copy(p.XId(), xId) }
func (p Packet) SetSecs(secs []byte) { copy(p.Secs(), secs) }
func (p Packet) SetFlags(flags []byte) { copy(p.Flags(), flags) }
func (p Packet) SetCIAddr(ip net.IP) { copy(p.CIAddr(), ip.To4()) }
func (p Packet) SetYIAddr(ip net.IP) { copy(p.YIAddr(), ip.To4()) }
func (p Packet) SetSIAddr(ip net.IP) { copy(p.SIAddr(), ip.To4()) }
func (p Packet) SetGIAddr(ip net.IP) { copy(p.GIAddr(), ip.To4()) }
// Parses the packet's options into an Options map
func (p Packet) ParseOptions() Options {
opts := p.Options()
options := make(Options, 10)
for len(opts) >= 2 && OptionCode(opts[0]) != End {
if OptionCode(opts[0]) == Pad {
opts = opts[1:]
continue
}
size := int(opts[1])
if len(opts) < 2+size {
break
}
options[OptionCode(opts[0])] = opts[2 : 2+size]
opts = opts[2+size:]
}
return options
}
func NewPacket(opCode OpCode) Packet {
p := make(Packet, 241)
p.SetOpCode(opCode)
p.SetHType(1) // Ethernet
p.SetCookie([]byte{99, 130, 83, 99})
p[240] = byte(End)
return p
}
// Appends a DHCP option to the end of a packet
func (p *Packet) AddOption(o OptionCode, value []byte) {
*p = append((*p)[:len(*p)-1], []byte{byte(o), byte(len(value))}...) // Strip off End, Add OptionCode and Length
*p = append(*p, value...) // Add Option Value
*p = append(*p, byte(End)) // Add on new End
}
// Removes all options from packet.
func (p *Packet) StripOptions() {
*p = append((*p)[:240], byte(End))
}
// Creates a request packet that a Client would send to a server.
func RequestPacket(mt MessageType, chAddr net.HardwareAddr, cIAddr net.IP, xId []byte, broadcast bool, options []Option) Packet {
p := NewPacket(BootRequest)
p.SetCHAddr(chAddr)
p.SetXId(xId)
if cIAddr != nil {
p.SetCIAddr(cIAddr)
}
p.SetBroadcast(broadcast)
p.AddOption(OptionDHCPMessageType, []byte{byte(mt)})
for _, o := range options {
p.AddOption(o.Code, o.Value)
}
p.PadToMinSize()
return p
}
// ReplyPacket creates a reply packet that a Server would send to a client.
// It uses the req Packet param to copy across common/necessary fields to
// associate the reply the request.
func ReplyPacket(req Packet, mt MessageType, serverId, yIAddr net.IP, leaseDuration time.Duration, options []Option) Packet {
p := NewPacket(BootReply)
p.SetXId(req.XId())
p.SetFlags(req.Flags())
p.SetYIAddr(yIAddr)
p.SetGIAddr(req.GIAddr())
p.SetCHAddr(req.CHAddr())
p.AddOption(OptionDHCPMessageType, []byte{byte(mt)})
p.AddOption(OptionServerIdentifier, []byte(serverId))
if leaseDuration > 0 {
p.AddOption(OptionIPAddressLeaseTime, OptionsLeaseTime(leaseDuration))
}
for _, o := range options {
p.AddOption(o.Code, o.Value)
}
p.PadToMinSize()
return p
}
// PadToMinSize pads a packet so that when sent over UDP, the entire packet,
// is 300 bytes (BOOTP min), to be compatible with really old devices.
var padder [272]byte
func (p *Packet) PadToMinSize() {
if n := len(*p); n < 272 {
*p = append(*p, padder[:272-n]...)
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"client.go",
"generatexid.go",
"inetsock.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"pktsock_linux.go",
],
"//conditions:default": [],
}),
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/d2g/dhcp4:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/golang.org/x/sys/unix:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
dhcp4client [![GoDoc](https://godoc.org/github.com/d2g/dhcp4client?status.svg)](http://godoc.org/github.com/d2g/dhcp4client) [![Coverage Status](https://coveralls.io/repos/d2g/dhcp4client/badge.svg?branch=HEAD)](https://coveralls.io/r/d2g/dhcp4client?branch=HEAD) [![Codeship Status for d2g/dhcp4client](https://codeship.com/projects/d75d9860-b364-0132-bc79-7e1d8cf367b9/status?branch=master)](https://codeship.com/projects/70187)
===========
DHCP Client
###### Thanks to:
@eyakubovich For AF_PACKET support.
package dhcp4client
import (
"bytes"
"net"
"time"
"github.com/d2g/dhcp4"
)
const (
MaxDHCPLen = 576
)
type Client struct {
hardwareAddr net.HardwareAddr //The HardwareAddr to send in the request.
ignoreServers []net.IP //List of Servers to Ignore requests from.
timeout time.Duration //Time before we timeout.
broadcast bool //Set the Bcast flag in BOOTP Flags
connection connection //The Connection Method to use
generateXID func([]byte) //Function Used to Generate a XID
}
//Abstracts the type of underlying socket used
type connection interface {
Close() error
Write(packet []byte) error
ReadFrom() ([]byte, net.IP, error)
SetReadTimeout(t time.Duration) error
}
func New(options ...func(*Client) error) (*Client, error) {
c := Client{
timeout: time.Second * 10,
broadcast: true,
generateXID: CryptoGenerateXID,
}
err := c.SetOption(options...)
if err != nil {
return nil, err
}
//if connection hasn't been set as an option create the default.
if c.connection == nil {
conn, err := NewInetSock()
if err != nil {
return nil, err
}
c.connection = conn
}
return &c, nil
}
func (c *Client) SetOption(options ...func(*Client) error) error {
for _, opt := range options {
if err := opt(c); err != nil {
return err
}
}
return nil
}
func Timeout(t time.Duration) func(*Client) error {
return func(c *Client) error {
c.timeout = t
return nil
}
}
func IgnoreServers(s []net.IP) func(*Client) error {
return func(c *Client) error {
c.ignoreServers = s
return nil
}
}
func HardwareAddr(h net.HardwareAddr) func(*Client) error {
return func(c *Client) error {
c.hardwareAddr = h
return nil
}
}
func Broadcast(b bool) func(*Client) error {
return func(c *Client) error {
c.broadcast = b
return nil
}
}
func Connection(conn connection) func(*Client) error {
return func(c *Client) error {
c.connection = conn
return nil
}
}
func GenerateXID(g func([]byte)) func(*Client) error {
return func(c *Client) error {
c.generateXID = g
return nil
}
}
//Close Connections
func (c *Client) Close() error {
if c.connection != nil {
return c.connection.Close()
}
return nil
}
//Send the Discovery Packet to the Broadcast Channel
func (c *Client) SendDiscoverPacket() (dhcp4.Packet, error) {
discoveryPacket := c.DiscoverPacket()
discoveryPacket.PadToMinSize()
return discoveryPacket, c.SendPacket(discoveryPacket)
}
//Retreive Offer...
//Wait for the offer for a specific Discovery Packet.
func (c *Client) GetOffer(discoverPacket *dhcp4.Packet) (dhcp4.Packet, error) {
for {
c.connection.SetReadTimeout(c.timeout)
readBuffer, source, err := c.connection.ReadFrom()
if err != nil {
return dhcp4.Packet{}, err
}
offerPacket := dhcp4.Packet(readBuffer)
offerPacketOptions := offerPacket.ParseOptions()
// Ignore Servers in my Ignore list
for _, ignoreServer := range c.ignoreServers {
if source.Equal(ignoreServer) {
continue
}
if offerPacket.SIAddr().Equal(ignoreServer) {
continue
}
}
if len(offerPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || dhcp4.MessageType(offerPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.Offer || !bytes.Equal(discoverPacket.XId(), offerPacket.XId()) {
continue
}
return offerPacket, nil
}
}
//Send Request Based On the offer Received.
func (c *Client) SendRequest(offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
requestPacket := c.RequestPacket(offerPacket)
requestPacket.PadToMinSize()
return requestPacket, c.SendPacket(requestPacket)
}
//Retreive Acknowledgement
//Wait for the offer for a specific Request Packet.
func (c *Client) GetAcknowledgement(requestPacket *dhcp4.Packet) (dhcp4.Packet, error) {
for {
c.connection.SetReadTimeout(c.timeout)
readBuffer, source, err := c.connection.ReadFrom()
if err != nil {
return dhcp4.Packet{}, err
}
acknowledgementPacket := dhcp4.Packet(readBuffer)
acknowledgementPacketOptions := acknowledgementPacket.ParseOptions()
// Ignore Servers in my Ignore list
for _, ignoreServer := range c.ignoreServers {
if source.Equal(ignoreServer) {
continue
}
if acknowledgementPacket.SIAddr().Equal(ignoreServer) {
continue
}
}
if !bytes.Equal(requestPacket.XId(), acknowledgementPacket.XId()) || len(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || (dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK && dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.NAK) {
continue
}
return acknowledgementPacket, nil
}
}
//Send Decline to the received acknowledgement.
func (c *Client) SendDecline(acknowledgementPacket *dhcp4.Packet) (dhcp4.Packet, error) {
declinePacket := c.DeclinePacket(acknowledgementPacket)
declinePacket.PadToMinSize()
return declinePacket, c.SendPacket(declinePacket)
}
//Send a DHCP Packet.
func (c *Client) SendPacket(packet dhcp4.Packet) error {
return c.connection.Write(packet)
}
//Create Discover Packet
func (c *Client) DiscoverPacket() dhcp4.Packet {
messageid := make([]byte, 4)
c.generateXID(messageid)
packet := dhcp4.NewPacket(dhcp4.BootRequest)
packet.SetCHAddr(c.hardwareAddr)
packet.SetXId(messageid)
packet.SetBroadcast(c.broadcast)
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Discover)})
//packet.PadToMinSize()
return packet
}
//Create Request Packet
func (c *Client) RequestPacket(offerPacket *dhcp4.Packet) dhcp4.Packet {
offerOptions := offerPacket.ParseOptions()
packet := dhcp4.NewPacket(dhcp4.BootRequest)
packet.SetCHAddr(c.hardwareAddr)
packet.SetXId(offerPacket.XId())
packet.SetCIAddr(offerPacket.CIAddr())
packet.SetSIAddr(offerPacket.SIAddr())
packet.SetBroadcast(c.broadcast)
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)})
packet.AddOption(dhcp4.OptionRequestedIPAddress, (offerPacket.YIAddr()).To4())
packet.AddOption(dhcp4.OptionServerIdentifier, offerOptions[dhcp4.OptionServerIdentifier])
return packet
}
//Create Request Packet For a Renew
func (c *Client) RenewalRequestPacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
messageid := make([]byte, 4)
c.generateXID(messageid)
acknowledgementOptions := acknowledgement.ParseOptions()
packet := dhcp4.NewPacket(dhcp4.BootRequest)
packet.SetCHAddr(acknowledgement.CHAddr())
packet.SetXId(messageid)
packet.SetCIAddr(acknowledgement.YIAddr())
packet.SetSIAddr(acknowledgement.SIAddr())
packet.SetBroadcast(c.broadcast)
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)})
packet.AddOption(dhcp4.OptionRequestedIPAddress, (acknowledgement.YIAddr()).To4())
packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
return packet
}
//Create Release Packet For a Release
func (c *Client) ReleasePacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
messageid := make([]byte, 4)
c.generateXID(messageid)
acknowledgementOptions := acknowledgement.ParseOptions()
packet := dhcp4.NewPacket(dhcp4.BootRequest)
packet.SetCHAddr(acknowledgement.CHAddr())
packet.SetXId(messageid)
packet.SetCIAddr(acknowledgement.YIAddr())
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Release)})
packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
return packet
}
//Create Decline Packet
func (c *Client) DeclinePacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
messageid := make([]byte, 4)
c.generateXID(messageid)
acknowledgementOptions := acknowledgement.ParseOptions()
packet := dhcp4.NewPacket(dhcp4.BootRequest)
packet.SetCHAddr(acknowledgement.CHAddr())
packet.SetXId(messageid)
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Decline)})
packet.AddOption(dhcp4.OptionRequestedIPAddress, (acknowledgement.YIAddr()).To4())
packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
return packet
}
//Lets do a Full DHCP Request.
func (c *Client) Request() (bool, dhcp4.Packet, error) {
discoveryPacket, err := c.SendDiscoverPacket()
if err != nil {
return false, discoveryPacket, err
}
offerPacket, err := c.GetOffer(&discoveryPacket)
if err != nil {
return false, offerPacket, err
}
requestPacket, err := c.SendRequest(&offerPacket)
if err != nil {
return false, requestPacket, err
}
acknowledgement, err := c.GetAcknowledgement(&requestPacket)
if err != nil {
return false, acknowledgement, err
}
acknowledgementOptions := acknowledgement.ParseOptions()
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
return false, acknowledgement, nil
}
return true, acknowledgement, nil
}
//Renew a lease backed on the Acknowledgement Packet.
//Returns Sucessfull, The AcknoledgementPacket, Any Errors
func (c *Client) Renew(acknowledgement dhcp4.Packet) (bool, dhcp4.Packet, error) {
renewRequest := c.RenewalRequestPacket(&acknowledgement)
renewRequest.PadToMinSize()
err := c.SendPacket(renewRequest)
if err != nil {
return false, renewRequest, err
}
newAcknowledgement, err := c.GetAcknowledgement(&renewRequest)
if err != nil {
return false, newAcknowledgement, err
}
newAcknowledgementOptions := newAcknowledgement.ParseOptions()
if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
return false, newAcknowledgement, nil
}
return true, newAcknowledgement, nil
}
//Release a lease backed on the Acknowledgement Packet.
//Returns Any Errors
func (c *Client) Release(acknowledgement dhcp4.Packet) error {
release := c.ReleasePacket(&acknowledgement)
release.PadToMinSize()
return c.SendPacket(release)
}
package dhcp4client
import (
cryptorand "crypto/rand"
mathrand "math/rand"
)
func CryptoGenerateXID(b []byte) {
if _, err := cryptorand.Read(b); err != nil {
panic(err)
}
}
func MathGenerateXID(b []byte) {
if _, err := mathrand.Read(b); err != nil {
panic(err)
}
}
package dhcp4client
import (
"net"
"time"
)
type inetSock struct {
*net.UDPConn
laddr net.UDPAddr
raddr net.UDPAddr
}
func NewInetSock(options ...func(*inetSock) error) (*inetSock, error) {
c := &inetSock{
laddr: net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 68},
raddr: net.UDPAddr{IP: net.IPv4bcast, Port: 67},
}
err := c.setOption(options...)
if err != nil {
return nil, err
}
conn, err := net.ListenUDP("udp4", &c.laddr)
if err != nil {
return nil, err
}
c.UDPConn = conn
return c, err
}
func (c *inetSock) setOption(options ...func(*inetSock) error) error {
for _, opt := range options {
if err := opt(c); err != nil {
return err
}
}
return nil
}
func SetLocalAddr(l net.UDPAddr) func(*inetSock) error {
return func(c *inetSock) error {
c.laddr = l
return nil
}
}
func SetRemoteAddr(r net.UDPAddr) func(*inetSock) error {
return func(c *inetSock) error {
c.raddr = r
return nil
}
}
func (c *inetSock) Write(packet []byte) error {
_, err := c.WriteToUDP(packet, &c.raddr)
return err
}
func (c *inetSock) ReadFrom() ([]byte, net.IP, error) {
readBuffer := make([]byte, MaxDHCPLen)
n, source, err := c.ReadFromUDP(readBuffer)
if source != nil {
return readBuffer[:n], source.IP, err
} else {
return readBuffer[:n], net.IP{}, err
}
}
func (c *inetSock) SetReadTimeout(t time.Duration) error {
return c.SetReadDeadline(time.Now().Add(t))
}
package dhcp4client
import (
"crypto/rand"
"encoding/binary"
"net"
"time"
"golang.org/x/sys/unix"
)
const (
minIPHdrLen = 20
maxIPHdrLen = 60
udpHdrLen = 8
ip4Ver = 0x40
ttl = 16
srcPort = 68
dstPort = 67
)
var (
bcastMAC = []byte{255, 255, 255, 255, 255, 255}
)
// abstracts AF_PACKET
type packetSock struct {
fd int
ifindex int
}
func NewPacketSock(ifindex int) (*packetSock, error) {
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, int(swap16(unix.ETH_P_IP)))
if err != nil {
return nil, err
}
addr := unix.SockaddrLinklayer{
Ifindex: ifindex,
Protocol: swap16(unix.ETH_P_IP),
}
if err = unix.Bind(fd, &addr); err != nil {
return nil, err
}
return &packetSock{
fd: fd,
ifindex: ifindex,
}, nil
}
func (pc *packetSock) Close() error {
return unix.Close(pc.fd)
}
func (pc *packetSock) Write(packet []byte) error {
lladdr := unix.SockaddrLinklayer{
Ifindex: pc.ifindex,
Protocol: swap16(unix.ETH_P_IP),
Halen: uint8(len(bcastMAC)),
}
copy(lladdr.Addr[:], bcastMAC)
pkt := make([]byte, minIPHdrLen+udpHdrLen+len(packet))
fillIPHdr(pkt[0:minIPHdrLen], udpHdrLen+uint16(len(packet)))
fillUDPHdr(pkt[minIPHdrLen:minIPHdrLen+udpHdrLen], uint16(len(packet)))
// payload
copy(pkt[minIPHdrLen+udpHdrLen:len(pkt)], packet)
return unix.Sendto(pc.fd, pkt, 0, &lladdr)
}
func (pc *packetSock) ReadFrom() ([]byte, net.IP, error) {
pkt := make([]byte, maxIPHdrLen+udpHdrLen+MaxDHCPLen)
n, _, err := unix.Recvfrom(pc.fd, pkt, 0)
if err != nil {
return nil, nil, err
}
// IP hdr len
ihl := int(pkt[0]&0x0F) * 4
// Source IP address
src := net.IP(pkt[12:16])
return pkt[ihl+udpHdrLen : n], src, nil
}
func (pc *packetSock) SetReadTimeout(t time.Duration) error {
tv := unix.NsecToTimeval(t.Nanoseconds())
return unix.SetsockoptTimeval(pc.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv)
}
// compute's 1's complement checksum
func chksum(p []byte, csum []byte) {
cklen := len(p)
s := uint32(0)
for i := 0; i < (cklen - 1); i += 2 {
s += uint32(p[i+1])<<8 | uint32(p[i])
}
if cklen&1 == 1 {
s += uint32(p[cklen-1])
}
s = (s >> 16) + (s & 0xffff)
s = s + (s >> 16)
s = ^s
csum[0] = uint8(s & 0xff)
csum[1] = uint8(s >> 8)
}
func fillIPHdr(hdr []byte, payloadLen uint16) {
// version + IHL
hdr[0] = ip4Ver | (minIPHdrLen / 4)
// total length
binary.BigEndian.PutUint16(hdr[2:4], uint16(len(hdr))+payloadLen)
// identification
if _, err := rand.Read(hdr[4:5]); err != nil {
panic(err)
}
// TTL
hdr[8] = 16
// Protocol
hdr[9] = unix.IPPROTO_UDP
// dst IP
copy(hdr[16:20], net.IPv4bcast.To4())
// compute IP hdr checksum
chksum(hdr[0:len(hdr)], hdr[10:12])
}
func fillUDPHdr(hdr []byte, payloadLen uint16) {
// src port
binary.BigEndian.PutUint16(hdr[0:2], srcPort)
// dest port
binary.BigEndian.PutUint16(hdr[2:4], dstPort)
// length
binary.BigEndian.PutUint16(hdr[4:6], udpHdrLen+payloadLen)
}
func swap16(x uint16) uint16 {
var b [2]byte
binary.BigEndian.PutUint16(b[:], x)
return binary.LittleEndian.Uint16(b[:])
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment