forked from tinkerbell/smee
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dhcp.go
103 lines (89 loc) · 2.9 KB
/
dhcp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package main
import (
"flag"
"runtime"
"github.com/avast/retry-go"
"github.com/gammazero/workerpool"
dhcp4 "github.com/packethost/dhcp4-go"
"github.com/packethost/pkg/env"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/tinkerbell/boots/conf"
"github.com/tinkerbell/boots/job"
"github.com/tinkerbell/boots/metrics"
)
var listenAddr = conf.BOOTPBind
func init() {
flag.StringVar(&listenAddr, "dhcp-addr", listenAddr, "IP and port to listen on for DHCP.")
}
// ServeDHCP is a useless comment
func ServeDHCP() {
poolSize := env.Int("BOOTS_DHCP_WORKERS", runtime.GOMAXPROCS(0)/2)
handler := dhcpHandler{pool: workerpool.New(poolSize)}
defer handler.pool.Stop()
err := retry.Do(
func() error {
return errors.Wrap(dhcp4.ListenAndServe(listenAddr, handler), "serving dhcp")
},
)
if err != nil {
mainlog.Fatal(errors.Wrap(err, "retry dhcp serve"))
}
}
type dhcpHandler struct {
pool *workerpool.WorkerPool
}
func (d dhcpHandler) ServeDHCP(w dhcp4.ReplyWriter, req *dhcp4.Packet) {
d.pool.Submit(func() { d.serveDHCP(w, req) })
}
func (d dhcpHandler) serveDHCP(w dhcp4.ReplyWriter, req *dhcp4.Packet) {
mac := req.GetCHAddr()
if conf.ShouldIgnoreOUI(mac.String()) {
mainlog.With("mac", mac).Info("mac is in ignore list")
return
}
gi := req.GetGIAddr()
if conf.ShouldIgnoreGI(gi.String()) {
mainlog.With("giaddr", gi).Info("giaddr is in ignore list")
return
}
metrics.DHCPTotal.WithLabelValues("recv", req.GetMessageType().String(), gi.String()).Inc()
labels := prometheus.Labels{"from": "dhcp", "op": req.GetMessageType().String()}
metrics.JobsTotal.With(labels).Inc()
metrics.JobsInProgress.With(labels).Inc()
timer := prometheus.NewTimer(metrics.JobDuration.With(labels))
circuitID, err := getCircuitID(req)
if err != nil {
mainlog.With("mac", mac, "err", err).Info("error parsing option82")
} else {
mainlog.With("mac", mac, "circuitID", circuitID).Info("parsed option82/circuitid")
}
j, err := job.CreateFromDHCP(mac, gi, circuitID)
if err != nil {
mainlog.With("type", req.GetMessageType(), "mac", mac, "err", err).Info("retrieved job is empty")
metrics.JobsInProgress.With(labels).Dec()
timer.ObserveDuration()
return
}
go func() {
if j.ServeDHCP(w, req) {
metrics.DHCPTotal.WithLabelValues("send", "DHCPOFFER", gi.String()).Inc()
}
metrics.JobsInProgress.With(labels).Dec()
timer.ObserveDuration()
}()
}
func getCircuitID(req *dhcp4.Packet) (string, error) {
var circuitID string
// Pulling option82 information from the packet (this is the relaying router)
// format: byte 1 is option number, byte 2 is length of the following array of bytes.
eightytwo, ok := req.GetOption(dhcp4.OptionRelayAgentInformation)
if ok {
if int(eightytwo[1]) < len(eightytwo) {
circuitID = string(eightytwo[2:eightytwo[1]])
} else {
return circuitID, errors.New("option82 option1 out of bounds (check eightytwo[1])")
}
}
return circuitID, nil
}