added ipv6api command

This commit is contained in:
Dmitri Dolguikh 2017-10-23 14:40:10 -07:00 committed by Dave Anderson
parent da6402caf7
commit 2c42665a0b
7 changed files with 145 additions and 65 deletions

View File

@ -10,19 +10,19 @@ import (
)
type BootConfiguration interface {
GetBootUrl(id []byte, clientArchType uint16) (string, error)
GetBootUrl(id []byte, clientArchType uint16) ([]byte, error)
}
type StaticBootConfiguration struct {
HttpBootUrl string
IPxeBootUrl string
HttpBootUrl []byte
IPxeBootUrl []byte
}
func MakeStaticBootConfiguration(httpBootUrl, ipxeBootUrl string) *StaticBootConfiguration {
return &StaticBootConfiguration{HttpBootUrl: httpBootUrl, IPxeBootUrl: ipxeBootUrl}
return &StaticBootConfiguration{HttpBootUrl: []byte(httpBootUrl), IPxeBootUrl: []byte(ipxeBootUrl)}
}
func (bc *StaticBootConfiguration) GetBootUrl(id []byte, clientArchType uint16) (string, error) {
func (bc *StaticBootConfiguration) GetBootUrl(id []byte, clientArchType uint16) ([]byte, error) {
if 0x10 == clientArchType {
return bc.HttpBootUrl, nil
}
@ -44,15 +44,15 @@ func MakeApiBootConfiguration(url string, timeout time.Duration) *ApiBootConfigu
}
}
func (bc *ApiBootConfiguration) GetBootUrl(id []byte, clientArchType uint16) (string, error) {
func (bc *ApiBootConfiguration) GetBootUrl(id []byte, clientArchType uint16) ([]byte, error) {
reqURL := fmt.Sprintf("%s/boot/%x/%d", bc.urlPrefix, id, clientArchType)
resp, err := bc.client.Get(reqURL)
if err != nil {
return "", err
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return "", fmt.Errorf("%s: %s", reqURL, http.StatusText(resp.StatusCode))
return nil, fmt.Errorf("%s: %s", reqURL, http.StatusText(resp.StatusCode))
}
defer resp.Body.Close()
@ -60,7 +60,7 @@ func (bc *ApiBootConfiguration) GetBootUrl(id []byte, clientArchType uint16) (st
buf.ReadFrom(resp.Body)
url, _ := bc.makeURLAbsolute(buf.String())
return url, nil
return []byte(url), nil
}
func (bc *ApiBootConfiguration) makeURLAbsolute(urlStr string) (string, error) {

View File

@ -128,28 +128,41 @@ func ShouldDiscardInformationRequest(p *Packet, serverDuid []byte) error {
return nil
}
func (b *PacketBuilder) BuildResponse(in *Packet) *Packet {
func (b *PacketBuilder) BuildResponse(in *Packet) (*Packet, error) {
switch in.Type {
case MsgSolicit:
association := b.Addresses.ReserveAddress(in.Options.ClientId(), in.Options.IaNaId())
bootFileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
if err != nil {
return nil, err
}
return b.MakeMsgAdvertise(in.TransactionID, in.Options.ClientId(), in.Options.IaNaId(),
in.Options.ClientArchType(), association.IpAddress)
in.Options.ClientArchType(), association.IpAddress, bootFileUrl), nil
case MsgRequest:
association := b.Addresses.ReserveAddress(in.Options.ClientId(), in.Options.IaNaId())
bootFileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
if err != nil {
return nil, err
}
return b.MakeMsgReply(in.TransactionID, in.Options.ClientId(), in.Options.IaNaId(),
in.Options.ClientArchType(), association.IpAddress)
in.Options.ClientArchType(), association.IpAddress, bootFileUrl), nil
case MsgInformationRequest:
bootFileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
if err != nil {
return nil, err
}
return b.MakeMsgInformationRequestReply(in.TransactionID, in.Options.ClientId(),
in.Options.ClientArchType())
in.Options.ClientArchType(), bootFileUrl), nil
case MsgRelease:
b.Addresses.ReleaseAddress(in.Options.ClientId(), in.Options.IaNaId())
return b.MakeMsgReleaseReply(in.TransactionID, in.Options.ClientId())
return b.MakeMsgReleaseReply(in.TransactionID, in.Options.ClientId()), nil
default:
return nil
return nil, nil
}
}
func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress []byte) *Packet {
func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress []byte,
bootFileUrl []byte) *Packet {
ret_options := make(Options)
ret_options.AddOption(MakeOption(OptClientId, clientId))
ret_options.AddOption(MakeIaNaOption(iaId, b.calculateT1(), b.calculateT2(),
@ -158,12 +171,7 @@ func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId [
if 0x10 == clientArchType { // HTTPClient
ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient
}
bootfileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(clientId), clientArchType)
if err != nil {
fmt.Printf("!!!!!!!!!!!!!!!!!!!!!!!! %s", err)
return nil
}
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte(bootfileUrl)))
ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl))
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
//ret_options.AddOption(OptBootfileParam, []byte("http://")
@ -172,7 +180,8 @@ func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId [
return &Packet{Type: MsgAdvertise, TransactionID: transactionId, Options: ret_options}
}
func (b *PacketBuilder) MakeMsgReply(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress []byte) *Packet {
func (b *PacketBuilder) MakeMsgReply(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress []byte,
bootFileUrl []byte) *Packet {
ret_options := make(Options)
ret_options.AddOption(MakeOption(OptClientId, clientId))
ret_options.AddOption(MakeIaNaOption(iaId, b.calculateT1(), b.calculateT2(),
@ -181,27 +190,20 @@ func (b *PacketBuilder) MakeMsgReply(transactionId [3]byte, clientId, iaId []byt
if 0x10 == clientArchType { // HTTPClient
ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient
}
bootfileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(clientId), clientArchType)
if err != nil {
return nil
}
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte(bootfileUrl)))
ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl))
return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options}
}
func (b *PacketBuilder) MakeMsgInformationRequestReply(transactionId [3]byte, clientId []byte, clientArchType uint16) *Packet {
func (b *PacketBuilder) MakeMsgInformationRequestReply(transactionId [3]byte, clientId []byte, clientArchType uint16,
bootFileUrl []byte) *Packet {
ret_options := make(Options)
ret_options.AddOption(MakeOption(OptClientId, clientId))
ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid))
if 0x10 == clientArchType { // HTTPClient
ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient
}
bootfileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(clientId), clientArchType)
if err != nil {
return nil
}
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte(bootfileUrl)))
ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl))
return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options}
}

View File

@ -11,12 +11,12 @@ func TestMakeMsgAdvertise(t *testing.T) {
expectedServerId := []byte("serverid")
transactionId := [3]byte{'1', '2', '3'}
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
expectedBootFileUrl := []byte("http://bootfileurl")
bootConfig := MakeStaticBootConfiguration("httpbootfileurl", "ipxebootfileurl")
builder := MakePacketBuilder(expectedServerId, 90, 100, bootConfig,
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp)
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp, expectedBootFileUrl)
if msg.Type != MsgAdvertise {
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
@ -51,6 +51,9 @@ func TestMakeMsgAdvertise(t *testing.T) {
if bootfileUrlOption == nil {
t.Fatalf("Bootfile URL option should be present")
}
if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) {
t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption)
}
iaNaOption := msg.Options[OptIaNa]
if iaNaOption == nil {
@ -63,22 +66,24 @@ func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) {
expectedServerId := []byte("serverid")
transactionId := [3]byte{'1', '2', '3'}
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
expectedBootFileUrl := []byte("http://bootfileurl")
bootConfig := MakeStaticBootConfiguration("httpbootfileurl", "ipxebootfileurl")
builder := MakePacketBuilder(expectedServerId, 90, 100, bootConfig,
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp)
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp, expectedBootFileUrl)
vendorClassOption := msg.Options[OptVendorClass]
if vendorClassOption == nil {
t.Fatalf("Vendor class option should be present")
}
bootfileUrlOption := msg.Options[OptBootfileUrl]
if bootfileUrlOption == nil {
t.Fatalf("Bootfile URL option should be present")
}
if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) {
t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption)
}
}
func TestMakeMsgReply(t *testing.T) {
@ -86,12 +91,12 @@ func TestMakeMsgReply(t *testing.T) {
expectedServerId := []byte("serverid")
transactionId := [3]byte{'1', '2', '3'}
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
expectedBootFileUrl := []byte("http://bootfileurl")
bootConfig := MakeStaticBootConfiguration("httpbootfileurl", "ipxebootfileurl")
builder := MakePacketBuilder(expectedServerId, 90, 100, bootConfig,
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp)
msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp, expectedBootFileUrl)
if msg.Type != MsgReply {
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
@ -126,6 +131,9 @@ func TestMakeMsgReply(t *testing.T) {
if bootfileUrlOption == nil {
t.Fatalf("Bootfile URL option should be present")
}
if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) {
t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption)
}
iaNaOption := msg.Options[OptIaNa]
if iaNaOption == nil {
@ -138,12 +146,12 @@ func TestMakeMsgReplyWithHttpClientArch(t *testing.T) {
expectedServerId := []byte("serverid")
transactionId := [3]byte{'1', '2', '3'}
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
expectedBootFileUrl := []byte("http://bootfileurl")
bootConfig := MakeStaticBootConfiguration("httpbootfileurl", "ipxebootfileurl")
builder := MakePacketBuilder(expectedServerId, 90, 100, bootConfig,
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp)
msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp, expectedBootFileUrl)
vendorClassOption := msg.Options[OptVendorClass]
if vendorClassOption == nil {
@ -154,18 +162,21 @@ func TestMakeMsgReplyWithHttpClientArch(t *testing.T) {
if bootfileUrlOption == nil {
t.Fatalf("Bootfile URL option should be present")
}
if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) {
t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption)
}
}
func TestMakeMsgInformationRequestReply(t *testing.T) {
expectedClientId := []byte("clientid")
expectedServerId := []byte("serverid")
transactionId := [3]byte{'1', '2', '3'}
expectedBootFileUrl := []byte("http://bootfileurl")
bootConfig := MakeStaticBootConfiguration("httpbootfileurl", "ipxebootfileurl")
builder := MakePacketBuilder(expectedServerId, 90, 100, bootConfig,
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x11)
msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x11, expectedBootFileUrl)
if msg.Type != MsgReply {
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
@ -200,18 +211,21 @@ func TestMakeMsgInformationRequestReply(t *testing.T) {
if bootfileUrlOption == nil {
t.Fatalf("Bootfile URL option should be present")
}
if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) {
t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption)
}
}
func TestMakeMsgInformationRequestReplyWithHttpClientArch(t *testing.T) {
expectedClientId := []byte("clientid")
expectedServerId := []byte("serverid")
transactionId := [3]byte{'1', '2', '3'}
expectedBootFileUrl := []byte("http://bootfileurl")
bootConfig := MakeStaticBootConfiguration("httpbootfileurl", "ipxebootfileurl")
builder := MakePacketBuilder(expectedServerId, 90, 100, bootConfig,
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x10)
msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x10, expectedBootFileUrl)
vendorClassOption := msg.Options[OptVendorClass]
if vendorClassOption == nil {
@ -222,6 +236,9 @@ func TestMakeMsgInformationRequestReplyWithHttpClientArch(t *testing.T) {
if bootfileUrlOption == nil {
t.Fatalf("Bootfile URL option should be present")
}
if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) {
t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption)
}
}
func TestMakeMsgReleaseReply(t *testing.T) {
@ -229,8 +246,7 @@ func TestMakeMsgReleaseReply(t *testing.T) {
expectedServerId := []byte("serverid")
transactionId := [3]byte{'1', '2', '3'}
bootConfig := MakeStaticBootConfiguration("httpbootfileurl", "ipxebootfileurl")
builder := MakePacketBuilder(expectedServerId, 90, 100, bootConfig,
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
msg := builder.MakeMsgReleaseReply(transactionId, expectedClientId)

View File

@ -4,6 +4,7 @@ import (
"github.com/spf13/cobra"
"fmt"
"go.universe.tf/netboot/pixiecorev6"
"go.universe.tf/netboot/dhcp6"
)
var bootIPv6Cmd = &cobra.Command{
@ -37,8 +38,7 @@ var bootIPv6Cmd = &cobra.Command{
}
s.Address = addr
s.IPxeUrl = ipxeUrl
s.HttpbootUrl = httpBootUrl
s.BootUrls = dhcp6.MakeStaticBootConfiguration(httpBootUrl, ipxeUrl)
fmt.Println(s.Serve())
},

View File

@ -0,0 +1,55 @@
package cli
import (
"github.com/spf13/cobra"
"fmt"
"go.universe.tf/netboot/pixiecorev6"
"go.universe.tf/netboot/dhcp6"
"time"
)
var ipv6ApiCmd = &cobra.Command{
Use: "ipv6api",
Short: "Boot a kernel and optional init ramdisks over IPv6 using api",
Run: func(cmd *cobra.Command, args []string) {
addr, err := cmd.Flags().GetString("listen-addr")
if err != nil {
fatalf("Error reading flag: %s", err)
}
apiUrl, err := cmd.Flags().GetString("api-request-url")
if err != nil {
fatalf("Error reading flag: %s", err)
}
apiTimeout, err := cmd.Flags().GetDuration("api-request-timeout")
if err != nil {
fatalf("Error reading flag: %s", err)
}
s := pixiecorev6.NewServerV6()
if addr == "" {
fatalf("Please specify address to listen on")
} else {
}
if apiUrl == "" {
fatalf("Please specify ipxe config file url")
}
s.Address = addr
s.BootUrls = dhcp6.MakeApiBootConfiguration(apiUrl, apiTimeout)
fmt.Println(s.Serve())
},
}
func serverv6ApiConfigFlags(cmd *cobra.Command) {
cmd.Flags().StringP("listen-addr", "", "", "IPv6 address to listen on")
cmd.Flags().StringP("api-request-url", "", "", "Ipv6-specific API server url")
}
func init() {
rootCmd.AddCommand(ipv6ApiCmd)
serverv6ApiConfigFlags(ipv6ApiCmd)
ipv6ApiCmd.Flags().Duration("api-request-timeout", 5*time.Second, "Timeout for request to the API server")
}

View File

@ -19,7 +19,15 @@ func (s *ServerV6) serveDHCP(conn *dhcp6.Conn, packetBuilder *dhcp6.PacketBuilde
s.log("dhcpv6", fmt.Sprintf("Received (%d) packet (%d): %s\n", pkt.Type, pkt.TransactionID, pkt.Options.HumanReadable()))
response := packetBuilder.BuildResponse(pkt)
response, err := packetBuilder.BuildResponse(pkt)
if err != nil {
s.log("dhcpv6", fmt.Sprintf("Error creating response for transaction: %s: %s", pkt.TransactionID, err))
continue
}
if response == nil {
s.log("dhcpv6", fmt.Sprintf("Don't know how to respond to packet type: %d (transaction id %s)", pkt.Type, pkt.TransactionID))
continue
}
marshalled_response, err := response.Marshal()
if err != nil {

View File

@ -13,8 +13,7 @@ type ServerV6 struct {
Address string
Port string
Duid []byte
IPxeUrl string
HttpbootUrl string
BootUrls dhcp6.BootConfiguration
errs chan error
@ -56,8 +55,8 @@ func (s *ServerV6) Serve() error {
addressPool := dhcp6.NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::10"), net.ParseIP("2001:db8:f00f:cafe::100"), 1850)
// bootConfiguration := dhcp6.MakeStaticBootConfiguration("http://[2001:db8:f00f:cafe::4]/bootx64.efi", "http://[2001:db8:f00f:cafe::4]/script.ipxe")
bootConfiguration := dhcp6.MakeApiBootConfiguration("http://[2001:db8:f00f:cafe::4]:8888/", 10 *time.Second)
packetBuilder := dhcp6.MakePacketBuilder(s.Duid, 1800, 1850, bootConfiguration, addressPool)
// bootConfiguration := dhcp6.MakeApiBootConfiguration("http://[2001:db8:f00f:cafe::4]:8888/", 10 *time.Second)
packetBuilder := dhcp6.MakePacketBuilder(s.Duid, 1800, 1850, s.BootUrls, addressPool)
go func() { s.errs <- s.serveDHCP(dhcp, packetBuilder) }()