summaryrefslogtreecommitdiffhomepage
path: root/pkg/dhcp/client.go
diff options
context:
space:
mode:
authorTamir Duberstein <tamird@google.com>2019-05-09 15:21:50 -0700
committerShentubot <shentubot@google.com>2019-05-09 15:23:03 -0700
commit0f4be95a336bd5dbf214bc40eb5d1bbb5fce36a4 (patch)
tree07f34cd1ef2ee67bc85da09fa75ce195d6c73c77 /pkg/dhcp/client.go
parentc3b6d4587edc1c04c86c2504b68a41b8c77fd202 (diff)
Remove dhcp client
This was upstreamed from Fuchsia, but it is pretty buggy and doesn't rely on any private APIs. Thus it can be checked into the Fuchsia source tree without forking netstack, where we can more easily iterate on (and eventually remove) it. PiperOrigin-RevId: 247506582 Change-Id: Ifb1b60c6c4941c374a59c5570a6a9cacf2468981
Diffstat (limited to 'pkg/dhcp/client.go')
-rw-r--r--pkg/dhcp/client.go348
1 files changed, 0 insertions, 348 deletions
diff --git a/pkg/dhcp/client.go b/pkg/dhcp/client.go
deleted file mode 100644
index b7cde3819..000000000
--- a/pkg/dhcp/client.go
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package dhcp
-
-import (
- "bytes"
- "context"
- "fmt"
- "sync"
- "time"
-
- "gvisor.googlesource.com/gvisor/pkg/rand"
- "gvisor.googlesource.com/gvisor/pkg/tcpip"
- tcpipHeader "gvisor.googlesource.com/gvisor/pkg/tcpip/header"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
- "gvisor.googlesource.com/gvisor/pkg/tcpip/transport/udp"
- "gvisor.googlesource.com/gvisor/pkg/waiter"
-)
-
-// Client is a DHCP client.
-type Client struct {
- stack *stack.Stack
- nicid tcpip.NICID
- linkAddr tcpip.LinkAddress
- acquiredFunc func(old, new tcpip.Address, cfg Config)
-
- mu sync.Mutex
- addr tcpip.Address
- cfg Config
- lease time.Duration
- cancelRenew func()
-}
-
-// NewClient creates a DHCP client.
-//
-// TODO: add s.LinkAddr(nicid) to *stack.Stack.
-func NewClient(s *stack.Stack, nicid tcpip.NICID, linkAddr tcpip.LinkAddress, acquiredFunc func(old, new tcpip.Address, cfg Config)) *Client {
- return &Client{
- stack: s,
- nicid: nicid,
- linkAddr: linkAddr,
- acquiredFunc: acquiredFunc,
- }
-}
-
-// Run starts the DHCP client.
-// It will periodically search for an IP address using the Request method.
-func (c *Client) Run(ctx context.Context) {
- go c.run(ctx)
-}
-
-func (c *Client) run(ctx context.Context) {
- defer func() {
- c.mu.Lock()
- defer c.mu.Unlock()
- if c.addr != "" {
- c.stack.RemoveAddress(c.nicid, c.addr)
- }
- }()
-
- var renewAddr tcpip.Address
- for {
- reqCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
- cfg, err := c.Request(reqCtx, renewAddr)
- cancel()
- if err != nil {
- select {
- case <-time.After(1 * time.Second):
- // loop and try again
- case <-ctx.Done():
- return
- }
- }
-
- c.mu.Lock()
- renewAddr = c.addr
- c.mu.Unlock()
-
- timer := time.NewTimer(cfg.LeaseLength)
- select {
- case <-ctx.Done():
- timer.Stop()
- return
- case <-timer.C:
- // loop and make a renewal request
- }
- }
-}
-
-// Address reports the IP address acquired by the DHCP client.
-func (c *Client) Address() tcpip.Address {
- c.mu.Lock()
- defer c.mu.Unlock()
- return c.addr
-}
-
-// Config reports the DHCP configuration acquired with the IP address lease.
-func (c *Client) Config() Config {
- c.mu.Lock()
- defer c.mu.Unlock()
- return c.cfg
-}
-
-// Request executes a DHCP request session.
-//
-// On success, it adds a new address to this client's TCPIP stack.
-// If the server sets a lease limit a timer is set to automatically
-// renew it.
-func (c *Client) Request(ctx context.Context, requestedAddr tcpip.Address) (cfg Config, reterr error) {
- // TODO(b/127321246): remove calls to {Add,Remove}Address when they're no
- // longer required to send and receive broadcast.
- if err := c.stack.AddAddressWithOptions(c.nicid, ipv4.ProtocolNumber, tcpipHeader.IPv4Any, stack.NeverPrimaryEndpoint); err != nil && err != tcpip.ErrDuplicateAddress {
- return Config{}, fmt.Errorf("dhcp: AddAddressWithOptions(): %s", err)
- }
- defer c.stack.RemoveAddress(c.nicid, tcpipHeader.IPv4Any)
-
- var wq waiter.Queue
- ep, err := c.stack.NewEndpoint(udp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
- if err != nil {
- return Config{}, fmt.Errorf("dhcp: NewEndpoint(): %s", err)
- }
- defer ep.Close()
- if err := ep.SetSockOpt(tcpip.BroadcastOption(1)); err != nil {
- return Config{}, fmt.Errorf("dhcp: SetSockOpt(BroadcastOption): %s", err)
- }
- if err := ep.Bind(tcpip.FullAddress{
- Addr: tcpipHeader.IPv4Any,
- Port: ClientPort,
- NIC: c.nicid,
- }); err != nil {
- return Config{}, fmt.Errorf("dhcp: Bind(): %s", err)
- }
-
- var xid [4]byte
- if _, err := rand.Read(xid[:]); err != nil {
- return Config{}, fmt.Errorf("dhcp: rand.Read(): %s", err)
- }
-
- // DHCPDISCOVERY
- discOpts := options{
- {optDHCPMsgType, []byte{byte(dhcpDISCOVER)}},
- {optParamReq, []byte{
- 1, // request subnet mask
- 3, // request router
- 15, // domain name
- 6, // domain name server
- }},
- }
- if requestedAddr != "" {
- discOpts = append(discOpts, option{optReqIPAddr, []byte(requestedAddr)})
- }
- var clientID []byte
- if len(c.linkAddr) == 6 {
- clientID = append(
- []byte{1}, // RFC 1700: Hardware Type [Ethernet = 1]
- c.linkAddr...,
- )
- discOpts = append(discOpts, option{optClientID, clientID})
- }
- h := make(header, headerBaseSize+discOpts.len()+1)
- h.init()
- h.setOp(opRequest)
- copy(h.xidbytes(), xid[:])
- h.setBroadcast()
- copy(h.chaddr(), c.linkAddr)
- h.setOptions(discOpts)
-
- serverAddr := &tcpip.FullAddress{
- Addr: tcpipHeader.IPv4Broadcast,
- Port: ServerPort,
- NIC: c.nicid,
- }
- wopts := tcpip.WriteOptions{
- To: serverAddr,
- }
- var resCh <-chan struct{}
- if _, resCh, err = ep.Write(tcpip.SlicePayload(h), wopts); err != nil && resCh == nil {
- return Config{}, fmt.Errorf("dhcp discovery write: %v", err)
- }
-
- if resCh != nil {
- select {
- case <-resCh:
- case <-ctx.Done():
- return Config{}, fmt.Errorf("dhcp client address resolution: %v", tcpip.ErrAborted)
- }
-
- if _, _, err := ep.Write(tcpip.SlicePayload(h), wopts); err != nil {
- return Config{}, fmt.Errorf("dhcp discovery write: %v", err)
- }
- }
-
- we, ch := waiter.NewChannelEntry(nil)
- wq.EventRegister(&we, waiter.EventIn)
- defer wq.EventUnregister(&we)
-
- // DHCPOFFER
- var opts options
- for {
- v, _, err := ep.Read(nil)
- if err == tcpip.ErrWouldBlock {
- select {
- case <-ch:
- continue
- case <-ctx.Done():
- return Config{}, fmt.Errorf("reading dhcp offer: %v", tcpip.ErrAborted)
- }
- }
- h = header(v)
- var valid bool
- var e error
- opts, valid, e = loadDHCPReply(h, dhcpOFFER, xid[:])
- if !valid {
- if e != nil {
- // TODO: handle all the errors?
- // TODO: report malformed server responses
- }
- continue
- }
- break
- }
-
- var ack bool
- if err := cfg.decode(opts); err != nil {
- return Config{}, fmt.Errorf("dhcp offer: %v", err)
- }
-
- // DHCPREQUEST
- addr := tcpip.Address(h.yiaddr())
- if err := c.stack.AddAddressWithOptions(c.nicid, ipv4.ProtocolNumber, addr, stack.FirstPrimaryEndpoint); err != nil {
- if err != tcpip.ErrDuplicateAddress {
- return Config{}, fmt.Errorf("adding address: %v", err)
- }
- }
- defer func() {
- if !ack || reterr != nil {
- c.stack.RemoveAddress(c.nicid, addr)
- addr = ""
- cfg = Config{Error: reterr}
- }
-
- c.mu.Lock()
- oldAddr := c.addr
- c.addr = addr
- c.cfg = cfg
- c.mu.Unlock()
-
- // Clean up addresses before calling acquiredFunc
- // so nothing else uses them by mistake.
- //
- // (The deferred RemoveAddress call above silently errors.)
- c.stack.RemoveAddress(c.nicid, tcpipHeader.IPv4Any)
-
- if c.acquiredFunc != nil {
- c.acquiredFunc(oldAddr, addr, cfg)
- }
- if requestedAddr != "" && requestedAddr != addr {
- c.stack.RemoveAddress(c.nicid, requestedAddr)
- }
- }()
- h.init()
- h.setOp(opRequest)
- for i, b := 0, h.yiaddr(); i < len(b); i++ {
- b[i] = 0
- }
- for i, b := 0, h.siaddr(); i < len(b); i++ {
- b[i] = 0
- }
- for i, b := 0, h.giaddr(); i < len(b); i++ {
- b[i] = 0
- }
- reqOpts := []option{
- {optDHCPMsgType, []byte{byte(dhcpREQUEST)}},
- {optReqIPAddr, []byte(addr)},
- {optDHCPServer, []byte(cfg.ServerAddress)},
- }
- if len(clientID) != 0 {
- reqOpts = append(reqOpts, option{optClientID, clientID})
- }
- h.setOptions(reqOpts)
- if _, _, err := ep.Write(tcpip.SlicePayload(h), wopts); err != nil {
- return Config{}, fmt.Errorf("dhcp discovery write: %v", err)
- }
-
- // DHCPACK
- for {
- v, _, err := ep.Read(nil)
- if err == tcpip.ErrWouldBlock {
- select {
- case <-ch:
- continue
- case <-ctx.Done():
- return Config{}, fmt.Errorf("reading dhcp ack: %v", tcpip.ErrAborted)
- }
- }
- h = header(v)
- var valid bool
- var e error
- opts, valid, e = loadDHCPReply(h, dhcpACK, xid[:])
- if !valid {
- if e != nil {
- // TODO: handle all the errors?
- // TODO: report malformed server responses
- }
- if opts, valid, _ = loadDHCPReply(h, dhcpNAK, xid[:]); valid {
- if msg := opts.message(); msg != "" {
- return Config{}, fmt.Errorf("dhcp: NAK %q", msg)
- }
- return Config{}, fmt.Errorf("dhcp: NAK with no message")
- }
- continue
- }
- break
- }
- ack = true
- return cfg, nil
-}
-
-func loadDHCPReply(h header, typ dhcpMsgType, xid []byte) (opts options, valid bool, err error) {
- if !h.isValid() || h.op() != opReply || !bytes.Equal(h.xidbytes(), xid[:]) {
- return nil, false, nil
- }
- opts, err = h.options()
- if err != nil {
- return nil, false, err
- }
- msgtype, err := opts.dhcpMsgType()
- if err != nil {
- return nil, false, err
- }
- if msgtype != typ {
- return nil, false, nil
- }
- return opts, true, nil
-}