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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
// Package server6 is a basic, extensible DHCPv6 server.
//
// To use the DHCPv6 server code you have to call NewServer with three arguments:
// - an interface to listen on,
// - 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("eth0", &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()
// }
//
package server6
import (
"log"
"net"
"os"
"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
logger Logger
}
// Serve starts the DHCPv6 server. The listener will run in background, and can
// be interrupted with `Server.Close`.
func (s *Server) Serve() error {
s.logger.Printf("Server listening on %s", s.conn.LocalAddr())
s.logger.Printf("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 {
s.logger.Printf("Error reading from packet conn: %v", err)
return err
}
s.logger.Printf("Handling request from %v", peer)
d, err := dhcpv6.FromBytes(rbuf[:n])
if err != nil {
s.logger.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`.
// * If `addr` is a multicast group, the group will be additionally joined
// * If `addr` is the wildcard address on the DHCPv6 server port (`[::]:547), the
// multicast groups All_DHCP_Relay_Agents_and_Servers(`[ff02::1:2]`) and
// All_DHCP_Servers(`[ff05::1:3]:547`) will be joined.
// * If `addr` is nil, IPv6 unspec on the DHCP server port is used and the above
// behaviour applies
// 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,
logger: EmptyLogger{},
}
for _, o := range opt {
o(s)
}
if s.conn != nil {
return s, nil
}
if addr == nil {
addr = &net.UDPAddr{
IP: net.IPv6unspecified,
Port: dhcpv6.DefaultServerPort,
}
}
var (
err error
iface *net.Interface
)
if ifname == "" {
iface = nil
} else {
iface, err = net.InterfaceByName(ifname)
if err != nil {
return nil, err
}
}
// no connection provided by the user, create a new one
s.conn, err = NewIPv6UDPConn(ifname, addr)
if err != nil {
return nil, err
}
p := ipv6.NewPacketConn(s.conn)
if addr.IP.IsMulticast() {
if err := p.JoinGroup(iface, addr); err != nil {
return nil, err
}
} else if (addr.IP == nil || addr.IP.IsUnspecified()) && addr.Port == dhcpv6.DefaultServerPort {
// For wildcard addresses on the correct port, listen on both multicast
// addresses defined in the RFC as a "default" behaviour
for _, g := range []net.IP{dhcpv6.AllDHCPRelayAgentsAndServers, dhcpv6.AllDHCPServers} {
group := net.UDPAddr{
IP: g,
Port: dhcpv6.DefaultServerPort,
}
if err := p.JoinGroup(iface, &group); err != nil {
return nil, err
}
}
}
return s, nil
}
// WithSummaryLogger logs one-line DHCPv6 message summaries when sent & received.
func WithSummaryLogger() ServerOpt {
return func(s *Server) {
s.logger = ShortSummaryLogger{
Printfer: log.New(os.Stderr, "[dhcpv6] ", log.LstdFlags),
}
}
}
// WithDebugLogger logs multi-line full DHCPv6 messages when sent & received.
func WithDebugLogger() ServerOpt {
return func(s *Server) {
s.logger = DebugLogger{
Printfer: log.New(os.Stderr, "[dhcpv6] ", log.LstdFlags),
}
}
}
// WithLogger set the logger (see interface Logger).
func WithLogger(newLogger Logger) ServerOpt {
return func(s *Server) {
s.logger = newLogger
}
}
|