summaryrefslogtreecommitdiffhomepage
path: root/netboot/netboot.go
diff options
context:
space:
mode:
authorMarco Guerri <marcoguerri@users.noreply.github.com>2018-11-07 23:38:39 +0000
committerinsomniac <insomniacslk@users.noreply.github.com>2018-11-07 23:38:39 +0000
commit4da458ed1d2abe3eaa398b551d91ac79c03682f9 (patch)
tree6429271745967c1ebee056c2a199a2467b945e90 /netboot/netboot.go
parent7af404c171f0b1e63112b579e246dd3dc77e56ce (diff)
Add netboot/netconf support for DHCPv4 (#185)
Diffstat (limited to 'netboot/netboot.go')
-rw-r--r--netboot/netboot.go63
1 files changed, 62 insertions, 1 deletions
diff --git a/netboot/netboot.go b/netboot/netboot.go
index 2f4cc8f..8bb9ac4 100644
--- a/netboot/netboot.go
+++ b/netboot/netboot.go
@@ -6,9 +6,14 @@ import (
"log"
"time"
+ "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv6"
)
+var sleeper = func(d time.Duration) {
+ time.Sleep(d)
+}
+
// RequestNetbootv6 sends a netboot request via DHCPv6 and returns the exchanged packets. Additional modifiers
// can be passed to manipulate both solicit and advertise packets.
func RequestNetbootv6(ifname string, timeout time.Duration, retries int, modifiers ...dhcpv6.Modifier) ([]dhcpv6.DHCPv6, error) {
@@ -36,7 +41,38 @@ func RequestNetbootv6(ifname string, timeout time.Duration, retries int, modifie
// don't wait at the end of the last attempt
break
}
- time.Sleep(delay)
+ sleeper(delay)
+ // TODO add random splay
+ delay = delay * 2
+ continue
+ }
+ break
+ }
+ return conversation, nil
+}
+
+// RequestNetbootv4 sends a netboot request via DHCPv4 and returns the exchanged packets. Additional modifiers
+// can be passed to manipulate both the discover and offer packets.
+func RequestNetbootv4(ifname string, timeout time.Duration, retries int, modifiers ...dhcpv4.Modifier) ([]*dhcpv4.DHCPv4, error) {
+ var (
+ conversation []*dhcpv4.DHCPv4
+ err error
+ )
+ delay := 2 * time.Second
+ modifiers = append(modifiers, dhcpv4.WithNetboot)
+ for i := 0; i <= retries; i++ {
+ log.Printf("sending request, attempt #%d", i+1)
+ client := dhcpv4.NewClient()
+ client.ReadTimeout = timeout
+ conversation, err = client.Exchange(ifname, nil, modifiers...)
+ if err != nil {
+ log.Printf("Client.Exchange failed: %v", err)
+ log.Printf("sleeping %v before retrying", delay)
+ if i >= retries {
+ // don't wait at the end of the last attempt
+ break
+ }
+ sleeper(delay)
// TODO add random splay
delay = delay * 2
continue
@@ -95,3 +131,28 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro
}
return netconf, bootfile, nil
}
+
+// ConversationToNetconfv4 extracts network configuration and boot file URL from a
+// DHCPv4 4-way conversation and returns them, or an error if any.
+func ConversationToNetconfv4(conversation []*dhcpv4.DHCPv4) (*NetConf, string, error) {
+ var reply *dhcpv4.DHCPv4
+ var bootFileUrl string
+ for _, m := range conversation {
+ // look for a BootReply packet of type Offer containing the bootfile URL.
+ // Normally both packets with Message Type OFFER or ACK do contain
+ // the bootfile URL.
+ if m.Opcode() == dhcpv4.OpcodeBootReply && *m.MessageType() == dhcpv4.MessageTypeOffer {
+ bootFileUrl = m.BootFileNameToString()
+ reply = m
+ break
+ }
+ }
+ if reply == nil {
+ return nil, "", errors.New("no OFFER with valid bootfile URL received")
+ }
+ netconf, err := GetNetConfFromPacketv4(reply)
+ if err != nil {
+ return nil, "", fmt.Errorf("could not get netconf: %v", err)
+ }
+ return netconf, bootFileUrl, nil
+}