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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
package server6
/*
To use the DHCPv6 server code you have to call NewServer with two arguments:
- an address to listen on, and
- a handler function, that will be called every time a valid DHCPv6 packet is
received.
The address to listen on is used to know IP address, port and optionally the
scope to create and UDP socket to listen on for DHCPv6 traffic.
The handler is a function that takes as input a packet connection, that can be
used to reply to the client; a peer address, that identifies the client sending
the request, and the DHCPv6 packet itself. Just implement your custom logic in
the handler.
Optionally, NewServer can receive options that will modify the server object.
Some options already exist, for example WithConn. If this option is passed with
a valid connection, the listening address argument is ignored.
Example program:
package main
import (
"log"
"net"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/dhcpv6/server6"
)
func handler(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6) {
// this function will just print the received DHCPv6 message, without replying
log.Print(m.Summary())
}
func main() {
laddr := net.UDPAddr{
IP: net.ParseIP("::1"),
Port: 547,
}
server, err := server6.NewServer(&laddr, handler)
if err != nil {
log.Fatal(err)
}
// This never returns. If you want to do other stuff, dump it into a
// goroutine.
server.Serve()
}
*/
import (
"log"
"net"
"github.com/insomniacslk/dhcp/dhcpv6"
"golang.org/x/net/ipv6"
)
// Handler is a type that defines the handler function to be called every time a
// valid DHCPv6 message is received
type Handler func(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6)
// Server represents a DHCPv6 server object
type Server struct {
conn net.PacketConn
handler Handler
}
// Serve starts the DHCPv6 server. The listener will run in background, and can
// be interrupted with `Server.Close`.
func (s *Server) Serve() error {
log.Printf("Server listening on %s", s.conn.LocalAddr())
log.Print("Ready to handle requests")
defer s.Close()
for {
rbuf := make([]byte, 4096) // FIXME this is bad
n, peer, err := s.conn.ReadFrom(rbuf)
if err != nil {
log.Printf("Error reading from packet conn: %v", err)
return err
}
log.Printf("Handling request from %v", peer)
d, err := dhcpv6.FromBytes(rbuf[:n])
if err != nil {
log.Printf("Error parsing DHCPv6 request: %v", err)
continue
}
go s.handler(s.conn, peer, d)
}
}
// Close sends a termination request to the server, and closes the UDP listener
func (s *Server) Close() error {
return s.conn.Close()
}
// A ServerOpt configures a Server.
type ServerOpt func(s *Server)
// WithConn configures a server with the given connection.
func WithConn(conn net.PacketConn) ServerOpt {
return func(s *Server) {
s.conn = conn
}
}
// NewServer initializes and returns a new Server object, listening on `addr`,
// and joining the multicast group ff02::1:2 . If `addr` is nil, IPv6 unspec is
// used. If `WithConn` is used with a non-nil address, `addr` and `ifname` have
// no effect. In such case, joining the multicast group is the caller's
// responsibility.
func NewServer(ifname string, addr *net.UDPAddr, handler Handler, opt ...ServerOpt) (*Server, error) {
s := &Server{
handler: handler,
}
for _, o := range opt {
o(s)
}
if s.conn == nil {
// no connection provided by the user, create a new one
conn, err := net.ListenUDP("udp6", addr)
if err != nil {
return nil, err
}
// join multicast group on the specified interface
var iface *net.Interface
if ifname == "" {
iface = nil
} else {
iface, err = net.InterfaceByName(ifname)
if err != nil {
return nil, err
}
}
group := net.UDPAddr{
IP: dhcpv6.AllDHCPRelayAgentsAndServers,
Port: dhcpv6.DefaultServerPort,
}
p := ipv6.NewPacketConn(conn)
if err := p.JoinGroup(iface, &group); err != nil {
return nil, err
}
s.conn = conn
}
return s, nil
}
|