Beispiel #1
0
func ServeProxyDHCP(port int, booter api.Booter) error {
	conn, err := net.ListenPacket("udp4", fmt.Sprintf(":%d", port))
	if err != nil {
		return err
	}
	defer conn.Close()
	l := ipv4.NewPacketConn(conn)
	if err = l.SetControlMessage(ipv4.FlagInterface, true); err != nil {
		return err
	}

	log.Log("ProxyDHCP", "Listening on port %d", port)
	buf := make([]byte, 1024)
	for {
		n, msg, addr, err := l.ReadFrom(buf)
		if err != nil {
			log.Log("ProxyDHCP", "Error reading from socket: %s", err)
			continue
		}

		udpAddr := addr.(*net.UDPAddr)
		udpAddr.IP = net.IPv4bcast

		req, err := ParseDHCP(buf[:n])
		if err != nil {
			log.Debug("ProxyDHCP", "ParseDHCP: %s", err)
			continue
		}

		if err = booter.ShouldBoot(req.MAC); err != nil {
			log.Debug("ProxyDHCP", "Not offering to boot %s: %s", req.MAC, err)
			continue
		}

		req.ServerIP, err = InterfaceIP(msg.IfIndex)
		if err != nil {
			log.Log("ProxyDHCP", "Couldn't find an IP address to use to reply to %s: %s", req.MAC, err)
			continue
		}

		log.Log("ProxyDHCP", "Offering to boot %s (via %s)", req.MAC, req.ServerIP)
		if _, err := l.WriteTo(OfferDHCP(req), &ipv4.ControlMessage{
			IfIndex: msg.IfIndex,
		}, udpAddr); err != nil {
			log.Log("ProxyDHCP", "Responding to %s: %s", req.MAC, err)
			continue
		}
	}
}
Beispiel #2
0
func ServePXE(pxePort, httpPort int) error {
	conn, err := net.ListenPacket("udp4", fmt.Sprintf(":%d", pxePort))
	if err != nil {
		return err
	}
	defer conn.Close()
	l := ipv4.NewPacketConn(conn)
	if err = l.SetControlMessage(ipv4.FlagInterface, true); err != nil {
		return err
	}

	log.Log("PXE", "Listening on port %d", pxePort)
	buf := make([]byte, 1024)
	for {
		n, msg, addr, err := l.ReadFrom(buf)
		if err != nil {
			log.Log("PXE", "Error reading from socket: %s", err)
			continue
		}

		req, err := ParsePXE(buf[:n])
		if err != nil {
			log.Debug("PXE", "ParsePXE: %s", err)
			continue
		}

		req.ServerIP, err = dhcp.InterfaceIP(msg.IfIndex)
		if err != nil {
			log.Log("PXE", "Couldn't find an IP address to use to reply to %s: %s", req.MAC, err)
			continue
		}
		req.HTTPServer = fmt.Sprintf("http://%s:%d/", req.ServerIP, httpPort)

		log.Log("PXE", "Chainloading %s (%s) to pxelinux (via %s)", req.MAC, req.ClientIP, req.ServerIP)

		if _, err := l.WriteTo(ReplyPXE(req), &ipv4.ControlMessage{
			IfIndex: msg.IfIndex,
		}, addr); err != nil {
			log.Log("PXE", "Responding to %s: %s", req.MAC, err)
			continue
		}
	}
}
Beispiel #3
0
func (s *httpServer) File(w http.ResponseWriter, r *http.Request) {
	encodedID := filepath.Base(r.URL.Path)
	id, err := base64.URLEncoding.DecodeString(encodedID)
	if err != nil {
		log.Log("http", "Bad base64 encoding for URL %q from %s: %s", r.URL, r.RemoteAddr, err)
		http.Error(w, "Malformed file ID", http.StatusBadRequest)
		return
	}
	f, pretty, err := s.booter.File(string(id))
	if err != nil {
		log.Log("HTTP", "Couldn't get byte stream for %q from %s: %s", r.URL, r.RemoteAddr, err)
		http.Error(w, "Couldn't get byte stream", http.StatusInternalServerError)
		return
	}
	defer f.Close()

	w.Header().Set("Content-Type", "application/octet-stream")
	written, err := io.Copy(w, f)
	if err != nil {
		log.Log("HTTP", "Error serving %s to %s: %s", pretty, r.RemoteAddr, err)
		return
	}
	log.Log("HTTP", "Sent %s to %s (%d bytes)", pretty, r.RemoteAddr, written)
}
Beispiel #4
0
func ServeHTTP(port int, booter api.Booter, ldlinux []byte) error {
	s := &httpServer{
		booter:  booter,
		ldlinux: ldlinux,
	}
	if _, err := io.ReadFull(rand.Reader, s.key[:]); err != nil {
		return fmt.Errorf("cannot initialize ephemeral signing key: %s", err)
	}

	http.HandleFunc("/ldlinux.c32", s.Ldlinux)
	http.HandleFunc("/pxelinux.cfg/", s.PxelinuxConfig)
	http.HandleFunc("/f/", s.File)

	log.Log("HTTP", "Listening on port %d", port)
	return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
Beispiel #5
0
func (s *httpServer) PxelinuxConfig(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")

	macStr := filepath.Base(r.URL.Path)
	errStr := fmt.Sprintf("%s requested a pxelinux config from URL %q, which does not include a MAC address", r.RemoteAddr, r.URL)
	if !strings.HasPrefix(macStr, "01-") {
		log.Debug("HTTP", errStr)
		http.Error(w, "Missing MAC address in request", http.StatusBadRequest)
		return
	}
	mac, err := net.ParseMAC(macStr[3:])
	if err != nil {
		log.Debug("HTTP", errStr)
		http.Error(w, "Malformed MAC address in request", http.StatusBadRequest)
		return
	}

	spec, err := s.booter.BootSpec(mac)
	if err != nil {
		// We have a machine sitting in pxelinux, but the Booter says
		// we shouldn't be netbooting. So, give it a config that tells
		// pxelinux to shut down PXE booting and continue with the
		// next local boot method.
		log.Debug("HTTP", "Telling pxelinux on %s (%s) to boot from disk because of API server verdict: %s", mac, r.RemoteAddr, err)
		w.Write([]byte(bootFromDisk))
		return
	}

	// The file IDs can be arbitrary blobs that make sense to the
	// Booter, but pxelinux speaks URL, so we need to encode the
	// blobs.
	spec.Kernel = "f/" + base64.URLEncoding.EncodeToString([]byte(spec.Kernel))
	for i := range spec.Initrd {
		spec.Initrd[i] = "f/" + base64.URLEncoding.EncodeToString([]byte(spec.Initrd[i]))
	}

	cfg := fmt.Sprintf(`
SAY %s
DEFAULT linux
LABEL linux
LINUX %s
APPEND initrd=%s %s
`, strings.Replace(limerick, "\n", "\nSAY ", -1), spec.Kernel, strings.Join(spec.Initrd, ","), spec.Cmdline)

	w.Write([]byte(cfg))
	log.Log("HTTP", "Sent pxelinux config to %s (%s)", mac, r.RemoteAddr)
}
Beispiel #6
0
func main() {
	flag.Parse()

	booter, err := pickBooter()
	if err != nil {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "\nERROR: %s\n", err)
		os.Exit(1)
	}

	pxelinux, err := assets.Asset("lpxelinux.0")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	ldlinux, err := assets.Asset("ldlinux.c32")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	go func() {
		log.Fatalln(dhcp.ServeProxyDHCP(*portDHCP, booter))
	}()
	go func() {
		log.Fatalln(pxe.ServePXE(*portPXE, *portHTTP))
	}()
	go func() {
		tftp.Log = func(msg string, args ...interface{}) { pixiecorelog.Log("TFTP", msg, args...) }
		tftp.Debug = func(msg string, args ...interface{}) { pixiecorelog.Debug("TFTP", msg, args...) }
		log.Fatalln(tftp.ListenAndServe("udp4", ":"+strconv.Itoa(*portTFTP), tftp.Blob(pxelinux)))
	}()
	go func() {
		log.Fatalln(http.ServeHTTP(*portHTTP, booter, ldlinux))
	}()
	pixiecorelog.RecordLogs(*debug)
}
Beispiel #7
0
func (s *httpServer) Ldlinux(w http.ResponseWriter, r *http.Request) {
	log.Debug("HTTP", "Starting send of ldlinux.c32 to %s (%d bytes)", r.RemoteAddr, len(s.ldlinux))
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Write(s.ldlinux)
	log.Log("HTTP", "Sent ldlinux.c32 to %s (%d bytes)", r.RemoteAddr, len(s.ldlinux))
}