fix(dhcp): can not renew an ip address
The dhcp server is systemd-networkd, and the dhcp plugin can request an ip but can not renew it. The systemd-networkd just ignore the renew request. ``` 2024/09/14 21:46:00 no DHCP packet received within 10s 2024/09/14 21:46:00 retrying in 31.529038 seconds 2024/09/14 21:46:42 no DHCP packet received within 10s 2024/09/14 21:46:42 retrying in 63.150490 seconds 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: no more tries 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: renewal time expired, rebinding 2024/09/14 21:47:45 Link "eth1" down. Attempting to set up 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: lease rebound, expiration is 2024-09-14 22:47:45.309270751 +0800 CST m=+11730.048516519 ``` Follow the https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.6, following options must not be sent in renew - Requested IP Address - Server Identifier Since the upstream code has been inactive for 6 years, we should switch to another dhcpv4 library. The new selected one is https://github.com/insomniacslk/dhcp. Signed-off-by: Songmin Li <lisongmin@protonmail.com>
This commit is contained in:
parent
e4950728ce
commit
d61e7e5e1f
4
.github/workflows/test.yaml
vendored
4
.github/workflows/test.yaml
vendored
@ -67,6 +67,10 @@ jobs:
|
||||
sudo apt-get install linux-modules-extra-$(uname -r)
|
||||
- name: Install nftables
|
||||
run: sudo apt-get install nftables
|
||||
- name: Install dnsmasq(dhcp server)
|
||||
run: |
|
||||
sudo apt-get install dnsmasq
|
||||
sudo systemctl disable --now dnsmasq
|
||||
- uses: actions/checkout@v4
|
||||
- name: setup go
|
||||
uses: actions/setup-go@v5
|
||||
|
11
go.mod
11
go.mod
@ -9,10 +9,8 @@ require (
|
||||
github.com/containernetworking/cni v1.2.3
|
||||
github.com/coreos/go-iptables v0.8.0
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
|
||||
github.com/d2g/dhcp4client v1.0.0
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/networkplumbing/go-nft v0.4.0
|
||||
github.com/onsi/ginkgo/v2 v2.20.2
|
||||
@ -28,17 +26,22 @@ require (
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.3 // indirect
|
||||
github.com/containerd/errdefs v0.1.0 // indirect
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/mdlayher/packet v1.1.2 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
|
24
go.sum
24
go.sum
@ -21,14 +21,6 @@ github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNs
|
||||
github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||
github.com/d2g/dhcp4client v1.0.0 h1:suYBsYZIkSlUMEz4TAYCczKf62IA2UWC+O8+KtdOhCo=
|
||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5 h1:+CpLbZIeUn94m02LdEKPcgErLJ347NUwxPKs5u8ieiY=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 h1:itqmmf1PFpC4n5JW+j4BU7X4MTfVurhYRTjODoPb2Y8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -68,10 +60,20 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
|
||||
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
|
||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/networkplumbing/go-nft v0.4.0 h1:kExVMwXW48DOAukkBwyI16h4uhE5lN9iMvQd52lpTyU=
|
||||
github.com/networkplumbing/go-nft v0.4.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||
@ -80,6 +82,8 @@ github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@ -99,6 +103,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
@ -129,6 +135,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1,135 +0,0 @@
|
||||
// Copyright 2021 CNI 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 main
|
||||
|
||||
import (
|
||||
"github.com/d2g/dhcp4"
|
||||
"github.com/d2g/dhcp4client"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxDHCPLen = 576
|
||||
)
|
||||
|
||||
// Send the Discovery Packet to the Broadcast Channel
|
||||
func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4.Packet, error) {
|
||||
discoveryPacket := c.DiscoverPacket()
|
||||
|
||||
for opt, data := range options {
|
||||
discoveryPacket.AddOption(opt, data)
|
||||
}
|
||||
|
||||
discoveryPacket.PadToMinSize()
|
||||
return discoveryPacket, c.SendPacket(discoveryPacket)
|
||||
}
|
||||
|
||||
// Send Request Based On the offer Received.
|
||||
func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
||||
requestPacket := c.RequestPacket(offerPacket)
|
||||
|
||||
for opt, data := range options {
|
||||
requestPacket.AddOption(opt, data)
|
||||
}
|
||||
|
||||
requestPacket.PadToMinSize()
|
||||
|
||||
return requestPacket, c.SendPacket(requestPacket)
|
||||
}
|
||||
|
||||
// Send Decline to the received acknowledgement.
|
||||
func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet, options dhcp4.Options) (dhcp4.Packet, error) {
|
||||
declinePacket := c.DeclinePacket(acknowledgementPacket)
|
||||
|
||||
for opt, data := range options {
|
||||
declinePacket.AddOption(opt, data)
|
||||
}
|
||||
|
||||
declinePacket.PadToMinSize()
|
||||
|
||||
return declinePacket, c.SendPacket(declinePacket)
|
||||
}
|
||||
|
||||
// Lets do a Full DHCP Request.
|
||||
func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
||||
discoveryPacket, err := DhcpSendDiscoverPacket(c, options)
|
||||
if err != nil {
|
||||
return false, discoveryPacket, err
|
||||
}
|
||||
|
||||
offerPacket, err := c.GetOffer(&discoveryPacket)
|
||||
if err != nil {
|
||||
return false, offerPacket, err
|
||||
}
|
||||
|
||||
requestPacket, err := DhcpSendRequest(c, options, &offerPacket)
|
||||
if err != nil {
|
||||
return false, requestPacket, err
|
||||
}
|
||||
|
||||
acknowledgement, err := c.GetAcknowledgement(&requestPacket)
|
||||
if err != nil {
|
||||
return false, acknowledgement, err
|
||||
}
|
||||
|
||||
acknowledgementOptions := acknowledgement.ParseOptions()
|
||||
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
|
||||
return false, acknowledgement, nil
|
||||
}
|
||||
|
||||
return true, acknowledgement, nil
|
||||
}
|
||||
|
||||
// Renew a lease backed on the Acknowledgement Packet.
|
||||
// Returns Successful, The AcknoledgementPacket, Any Errors
|
||||
func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
||||
renewRequest := c.RenewalRequestPacket(&acknowledgement)
|
||||
|
||||
for opt, data := range options {
|
||||
renewRequest.AddOption(opt, data)
|
||||
}
|
||||
|
||||
renewRequest.PadToMinSize()
|
||||
|
||||
err := c.SendPacket(renewRequest)
|
||||
if err != nil {
|
||||
return false, renewRequest, err
|
||||
}
|
||||
|
||||
newAcknowledgement, err := c.GetAcknowledgement(&renewRequest)
|
||||
if err != nil {
|
||||
return false, newAcknowledgement, err
|
||||
}
|
||||
|
||||
newAcknowledgementOptions := newAcknowledgement.ParseOptions()
|
||||
if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
|
||||
return false, newAcknowledgement, nil
|
||||
}
|
||||
|
||||
return true, newAcknowledgement, nil
|
||||
}
|
||||
|
||||
// Release a lease backed on the Acknowledgement Packet.
|
||||
// Returns Any Errors
|
||||
func DhcpRelease(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) error {
|
||||
release := c.ReleasePacket(&acknowledgement)
|
||||
|
||||
for opt, data := range options {
|
||||
release.AddOption(opt, data)
|
||||
}
|
||||
|
||||
release.PadToMinSize()
|
||||
|
||||
return c.SendPacket(release)
|
||||
}
|
@ -74,7 +74,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
|
||||
return fmt.Errorf("error parsing netconf: %v", err)
|
||||
}
|
||||
|
||||
optsRequesting, optsProviding, err := prepareOptions(args.Args, conf.IPAM.ProvideOptions, conf.IPAM.RequestOptions)
|
||||
opts, err := prepareOptions(args.Args, conf.IPAM.ProvideOptions, conf.IPAM.RequestOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -89,7 +89,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
|
||||
} else {
|
||||
hostNetns := d.hostNetnsPrefix + args.Netns
|
||||
l, err = AcquireLease(clientID, hostNetns, args.IfName,
|
||||
optsRequesting, optsProviding,
|
||||
opts,
|
||||
d.clientTimeout, d.clientResendMax, d.broadcast)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -61,8 +61,7 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
|
||||
})
|
||||
|
||||
// Start the DHCP server
|
||||
dhcpServerDone, err = dhcpServerStart(originalNS, 2, dhcpServerStopCh)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
dhcpServerDone = dhcpServerStart(originalNS, 2, dhcpServerStopCh)
|
||||
|
||||
// Start the DHCP client daemon
|
||||
dhcpPluginPath, err := exec.LookPath("dhcp")
|
||||
|
@ -25,10 +25,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/d2g/dhcp4"
|
||||
"github.com/d2g/dhcp4server"
|
||||
"github.com/d2g/dhcp4server/leasepool"
|
||||
"github.com/d2g/dhcp4server/leasepool/memorypool"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/vishvananda/netlink"
|
||||
@ -48,31 +44,43 @@ func getTmpDir() (string, error) {
|
||||
return tmpDir, err
|
||||
}
|
||||
|
||||
func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) (*sync.WaitGroup, error) {
|
||||
// Add the expected IP to the pool
|
||||
lp := memorypool.MemoryPool{}
|
||||
type DhcpServer struct {
|
||||
cmd *exec.Cmd
|
||||
|
||||
Expect(numLeases).To(BeNumerically(">", 0))
|
||||
// Currently tests only need at most 2
|
||||
Expect(numLeases).To(BeNumerically("<=", 2))
|
||||
startAddr net.IP
|
||||
endAddr net.IP
|
||||
leaseTime time.Duration
|
||||
}
|
||||
|
||||
// tests expect first lease to be at address 192.168.1.5
|
||||
for i := 5; i < numLeases+5; i++ {
|
||||
err := lp.AddLease(leasepool.Lease{IP: dhcp4.IPAdd(net.IPv4(192, 168, 1, byte(i)), 0)})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error adding IP to DHCP pool: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
dhcpServer, err := dhcp4server.New(
|
||||
net.IPv4(192, 168, 1, 1),
|
||||
&lp,
|
||||
dhcp4server.SetLocalAddr(net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 67}),
|
||||
dhcp4server.SetRemoteAddr(net.UDPAddr{IP: net.IPv4bcast, Port: 68}),
|
||||
dhcp4server.LeaseDuration(time.Minute*15),
|
||||
func (s *DhcpServer) Serve() error {
|
||||
s.cmd = exec.Command(
|
||||
"dnsmasq",
|
||||
"--no-daemon",
|
||||
"--dhcp-sequential-ip", // allocate IPs sequentially
|
||||
"--port=0", // disable DNS
|
||||
"--conf-file=-", // Do not read /etc/dnsmasq.conf
|
||||
fmt.Sprintf("--dhcp-range=%s,%s,%d", s.startAddr, s.endAddr, int(s.leaseTime.Seconds())),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create DHCP server: %v", err)
|
||||
s.cmd.Stdin = bytes.NewBufferString("")
|
||||
s.cmd.Stdout = os.Stdout
|
||||
s.cmd.Stderr = os.Stderr
|
||||
|
||||
return s.cmd.Start()
|
||||
}
|
||||
|
||||
func (s *DhcpServer) Stop() error {
|
||||
if err := s.cmd.Process.Kill(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := s.cmd.Process.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) *sync.WaitGroup {
|
||||
dhcpServer := &DhcpServer{
|
||||
startAddr: net.IPv4(192, 168, 1, 5),
|
||||
endAddr: net.IPv4(192, 168, 1, 5+uint8(numLeases)-1),
|
||||
leaseTime: 5 * time.Minute,
|
||||
}
|
||||
|
||||
stopWg := sync.WaitGroup{}
|
||||
@ -84,9 +92,10 @@ func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) (*sync.W
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err = netns.Do(func(ns.NetNS) error {
|
||||
err := netns.Do(func(ns.NetNS) error {
|
||||
startWg.Done()
|
||||
if err := dhcpServer.ListenAndServe(); err != nil {
|
||||
|
||||
if err := dhcpServer.Serve(); err != nil {
|
||||
// Log, but don't trap errors; the server will
|
||||
// always report an error when stopped
|
||||
GinkgoT().Logf("DHCP server finished with error: %v", err)
|
||||
@ -103,12 +112,12 @@ func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) (*sync.W
|
||||
go func() {
|
||||
startWg.Done()
|
||||
<-stopCh
|
||||
dhcpServer.Shutdown()
|
||||
dhcpServer.Stop()
|
||||
stopWg.Done()
|
||||
}()
|
||||
startWg.Wait()
|
||||
|
||||
return &stopWg, nil
|
||||
return &stopWg
|
||||
}
|
||||
|
||||
const (
|
||||
@ -200,8 +209,7 @@ var _ = Describe("DHCP Operations", func() {
|
||||
})
|
||||
|
||||
// Start the DHCP server
|
||||
dhcpServerDone, err = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
dhcpServerDone = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
||||
|
||||
// Start the DHCP client daemon
|
||||
dhcpPluginPath, err := exec.LookPath("dhcp")
|
||||
@ -517,8 +525,7 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
||||
})
|
||||
|
||||
// Start the DHCP server
|
||||
dhcpServerDone, err = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
dhcpServerDone = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
||||
|
||||
// Start the DHCP client daemon
|
||||
dhcpPluginPath, err := exec.LookPath("dhcp")
|
||||
|
@ -15,6 +15,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
@ -24,8 +25,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/d2g/dhcp4"
|
||||
"github.com/d2g/dhcp4client"
|
||||
dhcp4 "github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
@ -35,8 +36,9 @@ import (
|
||||
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
||||
// and randomized to +/- 1sec
|
||||
const (
|
||||
resendDelay0 = 4 * time.Second
|
||||
resendDelayMax = 62 * time.Second
|
||||
resendDelay0 = 4 * time.Second
|
||||
resendDelayMax = 62 * time.Second
|
||||
defaultLeaseTime = 60 * time.Minute
|
||||
)
|
||||
|
||||
// To speed up the retry for first few failures, we retry without
|
||||
@ -60,8 +62,7 @@ const (
|
||||
|
||||
type DHCPLease struct {
|
||||
clientID string
|
||||
ack *dhcp4.Packet
|
||||
opts dhcp4.Options
|
||||
latestLease *nclient4.Lease
|
||||
link netlink.Link
|
||||
renewalTime time.Time
|
||||
rebindingTime time.Time
|
||||
@ -73,21 +74,22 @@ type DHCPLease struct {
|
||||
stop chan struct{}
|
||||
check chan struct{}
|
||||
wg sync.WaitGroup
|
||||
cancelFunc context.CancelFunc
|
||||
ctx context.Context
|
||||
// list of requesting and providing options and if they are necessary / their value
|
||||
optsRequesting map[dhcp4.OptionCode]bool
|
||||
optsProviding map[dhcp4.OptionCode][]byte
|
||||
opts []dhcp4.Option
|
||||
}
|
||||
|
||||
var requestOptionsDefault = map[dhcp4.OptionCode]bool{
|
||||
dhcp4.OptionRouter: true,
|
||||
dhcp4.OptionSubnetMask: true,
|
||||
var requestOptionsDefault = []dhcp4.OptionCode{
|
||||
dhcp4.OptionRouter,
|
||||
dhcp4.OptionSubnetMask,
|
||||
}
|
||||
|
||||
func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptions []RequestOption) (
|
||||
map[dhcp4.OptionCode]bool, map[dhcp4.OptionCode][]byte, error,
|
||||
[]dhcp4.Option, error,
|
||||
) {
|
||||
var optsRequesting map[dhcp4.OptionCode]bool
|
||||
var optsProviding map[dhcp4.OptionCode][]byte
|
||||
var opts []dhcp4.Option
|
||||
|
||||
var err error
|
||||
// parse CNI args
|
||||
cniArgsParsed := map[string]string{}
|
||||
@ -100,46 +102,51 @@ func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptio
|
||||
|
||||
// parse providing options map
|
||||
var optParsed dhcp4.OptionCode
|
||||
optsProviding = make(map[dhcp4.OptionCode][]byte)
|
||||
for _, opt := range provideOptions {
|
||||
optParsed, err = parseOptionName(string(opt.Option))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||
return nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||
}
|
||||
if len(opt.Value) > 0 {
|
||||
if len(opt.Value) > 255 {
|
||||
return nil, nil, fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
|
||||
return nil, fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
|
||||
}
|
||||
optsProviding[optParsed] = []byte(opt.Value)
|
||||
opts = append(opts, dhcp4.Option{Code: optParsed, Value: dhcp4.String(opt.Value)})
|
||||
}
|
||||
if value, ok := cniArgsParsed[opt.ValueFromCNIArg]; ok {
|
||||
if len(value) > 255 {
|
||||
return nil, nil, fmt.Errorf("value too long for option %q from CNI_ARGS %q: %q", opt.Option, opt.ValueFromCNIArg, opt.Value)
|
||||
return nil, fmt.Errorf("value too long for option %q from CNI_ARGS %q: %q", opt.Option, opt.ValueFromCNIArg, opt.Value)
|
||||
}
|
||||
optsProviding[optParsed] = []byte(value)
|
||||
opts = append(opts, dhcp4.Option{Code: optParsed, Value: dhcp4.String(value)})
|
||||
}
|
||||
}
|
||||
|
||||
// parse necessary options map
|
||||
optsRequesting = make(map[dhcp4.OptionCode]bool)
|
||||
var optsRequesting dhcp4.OptionCodeList
|
||||
skipRequireDefault := false
|
||||
for _, opt := range requestOptions {
|
||||
if opt.SkipDefault {
|
||||
skipRequireDefault = true
|
||||
}
|
||||
if opt.Option == "" {
|
||||
continue
|
||||
}
|
||||
optParsed, err = parseOptionName(string(opt.Option))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||
return nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||
}
|
||||
optsRequesting[optParsed] = true
|
||||
optsRequesting.Add(optParsed)
|
||||
}
|
||||
for k, v := range requestOptionsDefault {
|
||||
// only set if not skipping default and this value does not exists
|
||||
if _, ok := optsRequesting[k]; !ok && !skipRequireDefault {
|
||||
optsRequesting[k] = v
|
||||
if !skipRequireDefault {
|
||||
for _, opt := range requestOptionsDefault {
|
||||
optsRequesting.Add(opt)
|
||||
}
|
||||
}
|
||||
return optsRequesting, optsProviding, err
|
||||
if len(optsRequesting) > 0 {
|
||||
opts = append(opts, dhcp4.Option{Code: dhcp4.OptionParameterRequestList, Value: optsRequesting})
|
||||
}
|
||||
|
||||
return opts, err
|
||||
}
|
||||
|
||||
// AcquireLease gets an DHCP lease and then maintains it in the background
|
||||
@ -147,19 +154,24 @@ func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptio
|
||||
// calling DHCPLease.Stop()
|
||||
func AcquireLease(
|
||||
clientID, netns, ifName string,
|
||||
optsRequesting map[dhcp4.OptionCode]bool, optsProviding map[dhcp4.OptionCode][]byte,
|
||||
opts []dhcp4.Option,
|
||||
timeout, resendMax time.Duration, broadcast bool,
|
||||
) (*DHCPLease, error) {
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
l := &DHCPLease{
|
||||
clientID: clientID,
|
||||
stop: make(chan struct{}),
|
||||
check: make(chan struct{}),
|
||||
timeout: timeout,
|
||||
resendMax: resendMax,
|
||||
broadcast: broadcast,
|
||||
optsRequesting: optsRequesting,
|
||||
optsProviding: optsProviding,
|
||||
clientID: clientID,
|
||||
stop: make(chan struct{}),
|
||||
check: make(chan struct{}),
|
||||
timeout: timeout,
|
||||
resendMax: resendMax,
|
||||
broadcast: broadcast,
|
||||
opts: opts,
|
||||
cancelFunc: cancel,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
log.Printf("%v: acquiring lease", clientID)
|
||||
@ -209,28 +221,20 @@ func (l *DHCPLease) Check() {
|
||||
l.check <- struct{}{}
|
||||
}
|
||||
|
||||
func (l *DHCPLease) getOptionsWithClientID() dhcp4.Options {
|
||||
opts := make(dhcp4.Options)
|
||||
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
|
||||
// client identifier's first byte is "type"
|
||||
newClientID := []byte{0}
|
||||
newClientID = append(newClientID, opts[dhcp4.OptionClientIdentifier]...)
|
||||
opts[dhcp4.OptionClientIdentifier] = newClientID
|
||||
return opts
|
||||
func withClientID(clientID string) dhcp4.Modifier {
|
||||
return func(d *dhcp4.DHCPv4) {
|
||||
optClientID := []byte{0}
|
||||
optClientID = append(optClientID, []byte(clientID)...)
|
||||
d.Options.Update(dhcp4.OptClientIdentifier(optClientID))
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DHCPLease) getAllOptions() dhcp4.Options {
|
||||
opts := l.getOptionsWithClientID()
|
||||
|
||||
for k, v := range l.optsProviding {
|
||||
opts[k] = v
|
||||
func withAllOptions(l *DHCPLease) dhcp4.Modifier {
|
||||
return func(d *dhcp4.DHCPv4) {
|
||||
for _, opt := range l.opts {
|
||||
d.Options.Update(opt)
|
||||
}
|
||||
}
|
||||
|
||||
opts[dhcp4.OptionParameterRequestList] = []byte{}
|
||||
for k := range l.optsRequesting {
|
||||
opts[dhcp4.OptionParameterRequestList] = append(opts[dhcp4.OptionParameterRequestList], byte(k))
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func (l *DHCPLease) acquire() error {
|
||||
@ -241,60 +245,39 @@ func (l *DHCPLease) acquire() error {
|
||||
}
|
||||
}
|
||||
|
||||
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
|
||||
c, err := newDHCPClient(l.link, l.timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
opts := l.getAllOptions()
|
||||
|
||||
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
||||
ok, ack, err := DhcpRequest(c, opts)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case !ok:
|
||||
return nil, fmt.Errorf("DHCP server NACK'd own offer")
|
||||
default:
|
||||
return &ack, nil
|
||||
}
|
||||
pkt, err := backoffRetry(l.resendMax, func() (*nclient4.Lease, error) {
|
||||
return c.Request(
|
||||
l.ctx,
|
||||
withClientID(l.clientID),
|
||||
withAllOptions(l),
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.commit(pkt)
|
||||
l.commit(pkt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) commit(ack *dhcp4.Packet) error {
|
||||
opts := ack.ParseOptions()
|
||||
func (l *DHCPLease) commit(lease *nclient4.Lease) {
|
||||
l.latestLease = lease
|
||||
ack := lease.ACK
|
||||
|
||||
leaseTime, err := parseLeaseTime(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rebindingTime, err := parseRebindingTime(opts)
|
||||
if err != nil || rebindingTime > leaseTime {
|
||||
// Per RFC 2131 Section 4.4.5, it should default to 85% of lease time
|
||||
rebindingTime = leaseTime * 85 / 100
|
||||
}
|
||||
|
||||
renewalTime, err := parseRenewalTime(opts)
|
||||
if err != nil || renewalTime > rebindingTime {
|
||||
// Per RFC 2131 Section 4.4.5, it should default to 50% of lease time
|
||||
renewalTime = leaseTime / 2
|
||||
}
|
||||
leaseTime := ack.IPAddressLeaseTime(defaultLeaseTime)
|
||||
rebindingTime := ack.IPAddressRebindingTime(leaseTime * 85 / 100)
|
||||
renewalTime := ack.IPAddressRenewalTime(leaseTime / 2)
|
||||
|
||||
now := time.Now()
|
||||
l.expireTime = now.Add(leaseTime)
|
||||
l.renewalTime = now.Add(renewalTime)
|
||||
l.rebindingTime = now.Add(rebindingTime)
|
||||
l.ack = ack
|
||||
l.opts = opts
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) maintain() {
|
||||
@ -362,44 +345,38 @@ func (l *DHCPLease) downIface() {
|
||||
}
|
||||
|
||||
func (l *DHCPLease) renew() error {
|
||||
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
|
||||
c, err := newDHCPClient(l.link, l.timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
opts := l.getAllOptions()
|
||||
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
||||
ok, ack, err := DhcpRenew(c, *l.ack, opts)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case !ok:
|
||||
return nil, fmt.Errorf("DHCP server did not renew lease")
|
||||
default:
|
||||
return &ack, nil
|
||||
}
|
||||
lease, err := backoffRetry(l.resendMax, func() (*nclient4.Lease, error) {
|
||||
return c.Renew(
|
||||
l.ctx,
|
||||
l.latestLease,
|
||||
withClientID(l.clientID),
|
||||
withAllOptions(l),
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.commit(pkt)
|
||||
l.commit(lease)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) release() error {
|
||||
log.Printf("%v: releasing lease", l.clientID)
|
||||
|
||||
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
|
||||
c, err := newDHCPClient(l.link, l.timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
opts := l.getOptionsWithClientID()
|
||||
|
||||
if err = DhcpRelease(c, *l.ack, opts); err != nil {
|
||||
if err = c.Release(l.latestLease, withClientID(l.clientID)); err != nil {
|
||||
return fmt.Errorf("failed to send DHCPRELEASE")
|
||||
}
|
||||
|
||||
@ -407,33 +384,47 @@ func (l *DHCPLease) release() error {
|
||||
}
|
||||
|
||||
func (l *DHCPLease) IPNet() (*net.IPNet, error) {
|
||||
mask := parseSubnetMask(l.opts)
|
||||
ack := l.latestLease.ACK
|
||||
|
||||
mask := ack.SubnetMask()
|
||||
if mask == nil {
|
||||
return nil, fmt.Errorf("DHCP option Subnet Mask not found in DHCPACK")
|
||||
}
|
||||
|
||||
return &net.IPNet{
|
||||
IP: l.ack.YIAddr(),
|
||||
IP: ack.YourIPAddr,
|
||||
Mask: mask,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) Gateway() net.IP {
|
||||
return parseRouter(l.opts)
|
||||
ack := l.latestLease.ACK
|
||||
gws := ack.Router()
|
||||
if len(gws) > 0 {
|
||||
return gws[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) Routes() []*types.Route {
|
||||
routes := []*types.Route{}
|
||||
|
||||
ack := l.latestLease.ACK
|
||||
|
||||
// RFC 3442 states that if Classless Static Routes (option 121)
|
||||
// exist, we ignore Static Routes (option 33) and the Router/Gateway.
|
||||
opt121Routes := parseCIDRRoutes(l.opts)
|
||||
opt121Routes := ack.ClasslessStaticRoute()
|
||||
if len(opt121Routes) > 0 {
|
||||
return append(routes, opt121Routes...)
|
||||
for _, r := range opt121Routes {
|
||||
routes = append(routes, &types.Route{Dst: *r.Dest, GW: r.Router})
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// Append Static Routes
|
||||
routes = append(routes, parseRoutes(l.opts)...)
|
||||
if ack.Options.Has(dhcp4.OptionStaticRoutingTable) {
|
||||
routes = append(routes, parseRoutes(ack.Options.Get(dhcp4.OptionStaticRoutingTable))...)
|
||||
}
|
||||
|
||||
// The CNI spec says even if there is a gateway specified, we must
|
||||
// add a default route in the routes section.
|
||||
@ -450,7 +441,7 @@ func jitter(span time.Duration) time.Duration {
|
||||
return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0))
|
||||
}
|
||||
|
||||
func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
|
||||
func backoffRetry(resendMax time.Duration, f func() (*nclient4.Lease, error)) (*nclient4.Lease, error) {
|
||||
baseDelay := resendDelay0
|
||||
var sleepTime time.Duration
|
||||
fastRetryLimit := resendFastMax
|
||||
@ -487,17 +478,8 @@ func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dh
|
||||
func newDHCPClient(
|
||||
link netlink.Link,
|
||||
timeout time.Duration,
|
||||
broadcast bool,
|
||||
) (*dhcp4client.Client, error) {
|
||||
pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dhcp4client.New(
|
||||
dhcp4client.HardwareAddr(link.Attrs().HardwareAddr),
|
||||
dhcp4client.Timeout(timeout),
|
||||
dhcp4client.Broadcast(broadcast),
|
||||
dhcp4client.Connection(pktsock),
|
||||
)
|
||||
clientOpts ...nclient4.ClientOpt,
|
||||
) (*nclient4.Client, error) {
|
||||
clientOpts = append(clientOpts, nclient4.WithTimeout(timeout))
|
||||
return nclient4.New(link.Attrs().Name, clientOpts...)
|
||||
}
|
||||
|
@ -15,13 +15,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/d2g/dhcp4"
|
||||
dhcp4 "github.com/insomniacslk/dhcp/dhcpv4"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
@ -31,8 +29,8 @@ var optionNameToID = map[string]dhcp4.OptionCode{
|
||||
"subnet-mask": dhcp4.OptionSubnetMask,
|
||||
"routers": dhcp4.OptionRouter,
|
||||
"host-name": dhcp4.OptionHostName,
|
||||
"user-class": dhcp4.OptionUserClass,
|
||||
"vendor-class-identifier": dhcp4.OptionVendorClassIdentifier,
|
||||
"user-class": dhcp4.OptionUserClassInformation,
|
||||
"vendor-class-identifier": dhcp4.OptionClassIdentifier,
|
||||
}
|
||||
|
||||
func parseOptionName(option string) (dhcp4.OptionCode, error) {
|
||||
@ -41,18 +39,9 @@ func parseOptionName(option string) (dhcp4.OptionCode, error) {
|
||||
}
|
||||
i, err := strconv.ParseUint(option, 10, 8)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Can not parse option: %w", err)
|
||||
return dhcp4.OptionPad, fmt.Errorf("Can not parse option: %w", err)
|
||||
}
|
||||
return dhcp4.OptionCode(i), nil
|
||||
}
|
||||
|
||||
func parseRouter(opts dhcp4.Options) net.IP {
|
||||
if opts, ok := opts[dhcp4.OptionRouter]; ok {
|
||||
if len(opts) == 4 {
|
||||
return net.IP(opts)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return dhcp4.GenericOptionCode(i), nil
|
||||
}
|
||||
|
||||
func classfulSubnet(sn net.IP) net.IPNet {
|
||||
@ -62,100 +51,22 @@ func classfulSubnet(sn net.IP) net.IPNet {
|
||||
}
|
||||
}
|
||||
|
||||
func parseRoutes(opts dhcp4.Options) []*types.Route {
|
||||
func parseRoutes(opt []byte) []*types.Route {
|
||||
// StaticRoutes format: pairs of:
|
||||
// Dest = 4 bytes; Classful IP subnet
|
||||
// Router = 4 bytes; IP address of router
|
||||
|
||||
routes := []*types.Route{}
|
||||
if opt, ok := opts[dhcp4.OptionStaticRoute]; ok {
|
||||
for len(opt) >= 8 {
|
||||
sn := opt[0:4]
|
||||
r := opt[4:8]
|
||||
rt := &types.Route{
|
||||
Dst: classfulSubnet(sn),
|
||||
GW: r,
|
||||
}
|
||||
routes = append(routes, rt)
|
||||
opt = opt[8:]
|
||||
for len(opt) >= 8 {
|
||||
sn := opt[0:4]
|
||||
r := opt[4:8]
|
||||
rt := &types.Route{
|
||||
Dst: classfulSubnet(sn),
|
||||
GW: r,
|
||||
}
|
||||
routes = append(routes, rt)
|
||||
opt = opt[8:]
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func parseCIDRRoutes(opts dhcp4.Options) []*types.Route {
|
||||
// See RFC4332 for format (http://tools.ietf.org/html/rfc3442)
|
||||
|
||||
routes := []*types.Route{}
|
||||
if opt, ok := opts[dhcp4.OptionClasslessRouteFormat]; ok {
|
||||
for len(opt) >= 5 {
|
||||
width := int(opt[0])
|
||||
if width > 32 {
|
||||
// error: can't have more than /32
|
||||
return nil
|
||||
}
|
||||
// network bits are compacted to avoid zeros
|
||||
octets := 0
|
||||
if width > 0 {
|
||||
octets = (width-1)/8 + 1
|
||||
}
|
||||
|
||||
if len(opt) < 1+octets+4 {
|
||||
// error: too short
|
||||
return nil
|
||||
}
|
||||
|
||||
sn := make([]byte, 4)
|
||||
copy(sn, opt[1:octets+1])
|
||||
|
||||
gw := net.IP(opt[octets+1 : octets+5])
|
||||
|
||||
rt := &types.Route{
|
||||
Dst: net.IPNet{
|
||||
IP: net.IP(sn),
|
||||
Mask: net.CIDRMask(width, 32),
|
||||
},
|
||||
GW: gw,
|
||||
}
|
||||
routes = append(routes, rt)
|
||||
|
||||
opt = opt[octets+5:]
|
||||
}
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func parseSubnetMask(opts dhcp4.Options) net.IPMask {
|
||||
mask, ok := opts[dhcp4.OptionSubnetMask]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return net.IPMask(mask)
|
||||
}
|
||||
|
||||
func parseDuration(opts dhcp4.Options, code dhcp4.OptionCode, optName string) (time.Duration, error) {
|
||||
val, ok := opts[code]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("option %v not found", optName)
|
||||
}
|
||||
if len(val) != 4 {
|
||||
return 0, fmt.Errorf("option %v is not 4 bytes", optName)
|
||||
}
|
||||
|
||||
secs := binary.BigEndian.Uint32(val)
|
||||
return time.Duration(secs) * time.Second, nil
|
||||
}
|
||||
|
||||
func parseLeaseTime(opts dhcp4.Options) (time.Duration, error) {
|
||||
return parseDuration(opts, dhcp4.OptionIPAddressLeaseTime, "LeaseTime")
|
||||
}
|
||||
|
||||
func parseRenewalTime(opts dhcp4.Options) (time.Duration, error) {
|
||||
return parseDuration(opts, dhcp4.OptionRenewalTimeValue, "RenewalTime")
|
||||
}
|
||||
|
||||
func parseRebindingTime(opts dhcp4.Options) (time.Duration, error) {
|
||||
return parseDuration(opts, dhcp4.OptionRebindingTimeValue, "RebindingTime")
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/d2g/dhcp4"
|
||||
dhcp4 "github.com/insomniacslk/dhcp/dhcpv4"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
@ -61,17 +61,8 @@ func validateRoutes(t *testing.T, routes []*types.Route) {
|
||||
}
|
||||
|
||||
func TestParseRoutes(t *testing.T) {
|
||||
opts := make(dhcp4.Options)
|
||||
opts[dhcp4.OptionStaticRoute] = []byte{10, 0, 0, 0, 10, 1, 2, 3, 192, 168, 1, 0, 192, 168, 2, 3}
|
||||
routes := parseRoutes(opts)
|
||||
|
||||
validateRoutes(t, routes)
|
||||
}
|
||||
|
||||
func TestParseCIDRRoutes(t *testing.T) {
|
||||
opts := make(dhcp4.Options)
|
||||
opts[dhcp4.OptionClasslessRouteFormat] = []byte{8, 10, 10, 1, 2, 3, 24, 192, 168, 1, 192, 168, 2, 3}
|
||||
routes := parseCIDRRoutes(opts)
|
||||
data := []byte{10, 0, 0, 0, 10, 1, 2, 3, 192, 168, 1, 0, 192, 168, 2, 3}
|
||||
routes := parseRoutes(data)
|
||||
|
||||
validateRoutes(t, routes)
|
||||
}
|
||||
@ -87,10 +78,10 @@ func TestParseOptionName(t *testing.T) {
|
||||
"hostname", "host-name", dhcp4.OptionHostName, false,
|
||||
},
|
||||
{
|
||||
"hostname in number", "12", dhcp4.OptionHostName, false,
|
||||
"hostname in number", "12", dhcp4.GenericOptionCode(12), false,
|
||||
},
|
||||
{
|
||||
"random string", "doNotparseMe", 0, true,
|
||||
"random string", "doNotparseMe", dhcp4.OptionPad, true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
5
vendor/github.com/d2g/dhcp4/README.md
generated
vendored
5
vendor/github.com/d2g/dhcp4/README.md
generated
vendored
@ -1,5 +0,0 @@
|
||||
# DHCP4 - A DHCP library written in Go.
|
||||
|
||||
Warning: This library is still being developed. Function calls will change.
|
||||
|
||||
I've removed Server Functionality, for me this project supports the underlying DHCP format not the implementation.
|
121
vendor/github.com/d2g/dhcp4/constants.go
generated
vendored
121
vendor/github.com/d2g/dhcp4/constants.go
generated
vendored
@ -1,121 +0,0 @@
|
||||
package dhcp4
|
||||
|
||||
// OpCodes
|
||||
const (
|
||||
BootRequest OpCode = 1 // From Client
|
||||
BootReply OpCode = 2 // From Server
|
||||
)
|
||||
|
||||
// DHCP Message Type 53
|
||||
const (
|
||||
Discover MessageType = 1 // Broadcast Packet From Client - Can I have an IP?
|
||||
Offer MessageType = 2 // Broadcast From Server - Here's an IP
|
||||
Request MessageType = 3 // Broadcast From Client - I'll take that IP (Also start for renewals)
|
||||
Decline MessageType = 4 // Broadcast From Client - Sorry I can't use that IP
|
||||
ACK MessageType = 5 // From Server, Yes you can have that IP
|
||||
NAK MessageType = 6 // From Server, No you cannot have that IP
|
||||
Release MessageType = 7 // From Client, I don't need that IP anymore
|
||||
Inform MessageType = 8 // From Client, I have this IP and there's nothing you can do about it
|
||||
)
|
||||
|
||||
// DHCP Options
|
||||
const (
|
||||
End OptionCode = 255
|
||||
Pad OptionCode = 0
|
||||
OptionSubnetMask OptionCode = 1
|
||||
OptionTimeOffset OptionCode = 2
|
||||
OptionRouter OptionCode = 3
|
||||
OptionTimeServer OptionCode = 4
|
||||
OptionNameServer OptionCode = 5
|
||||
OptionDomainNameServer OptionCode = 6
|
||||
OptionLogServer OptionCode = 7
|
||||
OptionCookieServer OptionCode = 8
|
||||
OptionLPRServer OptionCode = 9
|
||||
OptionImpressServer OptionCode = 10
|
||||
OptionResourceLocationServer OptionCode = 11
|
||||
OptionHostName OptionCode = 12
|
||||
OptionBootFileSize OptionCode = 13
|
||||
OptionMeritDumpFile OptionCode = 14
|
||||
OptionDomainName OptionCode = 15
|
||||
OptionSwapServer OptionCode = 16
|
||||
OptionRootPath OptionCode = 17
|
||||
OptionExtensionsPath OptionCode = 18
|
||||
|
||||
// IP Layer Parameters per Host
|
||||
OptionIPForwardingEnableDisable OptionCode = 19
|
||||
OptionNonLocalSourceRoutingEnableDisable OptionCode = 20
|
||||
OptionPolicyFilter OptionCode = 21
|
||||
OptionMaximumDatagramReassemblySize OptionCode = 22
|
||||
OptionDefaultIPTimeToLive OptionCode = 23
|
||||
OptionPathMTUAgingTimeout OptionCode = 24
|
||||
OptionPathMTUPlateauTable OptionCode = 25
|
||||
|
||||
// IP Layer Parameters per Interface
|
||||
OptionInterfaceMTU OptionCode = 26
|
||||
OptionAllSubnetsAreLocal OptionCode = 27
|
||||
OptionBroadcastAddress OptionCode = 28
|
||||
OptionPerformMaskDiscovery OptionCode = 29
|
||||
OptionMaskSupplier OptionCode = 30
|
||||
OptionPerformRouterDiscovery OptionCode = 31
|
||||
OptionRouterSolicitationAddress OptionCode = 32
|
||||
OptionStaticRoute OptionCode = 33
|
||||
|
||||
// Link Layer Parameters per Interface
|
||||
OptionTrailerEncapsulation OptionCode = 34
|
||||
OptionARPCacheTimeout OptionCode = 35
|
||||
OptionEthernetEncapsulation OptionCode = 36
|
||||
|
||||
// TCP Parameters
|
||||
OptionTCPDefaultTTL OptionCode = 37
|
||||
OptionTCPKeepaliveInterval OptionCode = 38
|
||||
OptionTCPKeepaliveGarbage OptionCode = 39
|
||||
|
||||
// Application and Service Parameters
|
||||
OptionNetworkInformationServiceDomain OptionCode = 40
|
||||
OptionNetworkInformationServers OptionCode = 41
|
||||
OptionNetworkTimeProtocolServers OptionCode = 42
|
||||
OptionVendorSpecificInformation OptionCode = 43
|
||||
OptionNetBIOSOverTCPIPNameServer OptionCode = 44
|
||||
OptionNetBIOSOverTCPIPDatagramDistributionServer OptionCode = 45
|
||||
OptionNetBIOSOverTCPIPNodeType OptionCode = 46
|
||||
OptionNetBIOSOverTCPIPScope OptionCode = 47
|
||||
OptionXWindowSystemFontServer OptionCode = 48
|
||||
OptionXWindowSystemDisplayManager OptionCode = 49
|
||||
OptionNetworkInformationServicePlusDomain OptionCode = 64
|
||||
OptionNetworkInformationServicePlusServers OptionCode = 65
|
||||
OptionMobileIPHomeAgent OptionCode = 68
|
||||
OptionSimpleMailTransportProtocol OptionCode = 69
|
||||
OptionPostOfficeProtocolServer OptionCode = 70
|
||||
OptionNetworkNewsTransportProtocol OptionCode = 71
|
||||
OptionDefaultWorldWideWebServer OptionCode = 72
|
||||
OptionDefaultFingerServer OptionCode = 73
|
||||
OptionDefaultInternetRelayChatServer OptionCode = 74
|
||||
OptionStreetTalkServer OptionCode = 75
|
||||
OptionStreetTalkDirectoryAssistance OptionCode = 76
|
||||
|
||||
// DHCP Extensions
|
||||
OptionRequestedIPAddress OptionCode = 50
|
||||
OptionIPAddressLeaseTime OptionCode = 51
|
||||
OptionOverload OptionCode = 52
|
||||
OptionDHCPMessageType OptionCode = 53
|
||||
OptionServerIdentifier OptionCode = 54
|
||||
OptionParameterRequestList OptionCode = 55
|
||||
OptionMessage OptionCode = 56
|
||||
OptionMaximumDHCPMessageSize OptionCode = 57
|
||||
OptionRenewalTimeValue OptionCode = 58
|
||||
OptionRebindingTimeValue OptionCode = 59
|
||||
OptionVendorClassIdentifier OptionCode = 60
|
||||
OptionClientIdentifier OptionCode = 61
|
||||
|
||||
OptionTFTPServerName OptionCode = 66
|
||||
OptionBootFileName OptionCode = 67
|
||||
|
||||
OptionUserClass OptionCode = 77
|
||||
|
||||
OptionClientArchitecture OptionCode = 93
|
||||
|
||||
OptionTZPOSIXString OptionCode = 100
|
||||
OptionTZDatabaseString OptionCode = 101
|
||||
|
||||
OptionClasslessRouteFormat OptionCode = 121
|
||||
)
|
58
vendor/github.com/d2g/dhcp4/helpers.go
generated
vendored
58
vendor/github.com/d2g/dhcp4/helpers.go
generated
vendored
@ -1,58 +0,0 @@
|
||||
package dhcp4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IPRange returns how many ips in the ip range from start to stop (inclusive)
|
||||
func IPRange(start, stop net.IP) int {
|
||||
//return int(Uint([]byte(stop))-Uint([]byte(start))) + 1
|
||||
return int(binary.BigEndian.Uint32(stop.To4())) - int(binary.BigEndian.Uint32(start.To4())) + 1
|
||||
}
|
||||
|
||||
// IPAdd returns a copy of start + add.
|
||||
// IPAdd(net.IP{192,168,1,1},30) returns net.IP{192.168.1.31}
|
||||
func IPAdd(start net.IP, add int) net.IP { // IPv4 only
|
||||
start = start.To4()
|
||||
//v := Uvarint([]byte(start))
|
||||
result := make(net.IP, 4)
|
||||
binary.BigEndian.PutUint32(result, binary.BigEndian.Uint32(start)+uint32(add))
|
||||
//PutUint([]byte(result), v+uint64(add))
|
||||
return result
|
||||
}
|
||||
|
||||
// IPLess returns where IP a is less than IP b.
|
||||
func IPLess(a, b net.IP) bool {
|
||||
b = b.To4()
|
||||
for i, ai := range a.To4() {
|
||||
if ai != b[i] {
|
||||
return ai < b[i]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IPInRange returns true if ip is between (inclusive) start and stop.
|
||||
func IPInRange(start, stop, ip net.IP) bool {
|
||||
return !(IPLess(ip, start) || IPLess(stop, ip))
|
||||
}
|
||||
|
||||
// OptionsLeaseTime - converts a time.Duration to a 4 byte slice, compatible
|
||||
// with OptionIPAddressLeaseTime.
|
||||
func OptionsLeaseTime(d time.Duration) []byte {
|
||||
leaseBytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(leaseBytes, uint32(d/time.Second))
|
||||
//PutUvarint(leaseBytes, uint64(d/time.Second))
|
||||
return leaseBytes
|
||||
}
|
||||
|
||||
// JoinIPs returns a byte slice of IP addresses, one immediately after the other
|
||||
// This may be useful for creating multiple IP options such as OptionRouter.
|
||||
func JoinIPs(ips []net.IP) (b []byte) {
|
||||
for _, v := range ips {
|
||||
b = append(b, v.To4()...)
|
||||
}
|
||||
return
|
||||
}
|
40
vendor/github.com/d2g/dhcp4/option.go
generated
vendored
40
vendor/github.com/d2g/dhcp4/option.go
generated
vendored
@ -1,40 +0,0 @@
|
||||
package dhcp4
|
||||
|
||||
type OptionCode byte
|
||||
|
||||
type Option struct {
|
||||
Code OptionCode
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// Map of DHCP options
|
||||
type Options map[OptionCode][]byte
|
||||
|
||||
// SelectOrderOrAll has same functionality as SelectOrder, except if the order
|
||||
// param is nil, whereby all options are added (in arbitrary order).
|
||||
func (o Options) SelectOrderOrAll(order []byte) []Option {
|
||||
if order == nil {
|
||||
opts := make([]Option, 0, len(o))
|
||||
for i, v := range o {
|
||||
opts = append(opts, Option{Code: i, Value: v})
|
||||
}
|
||||
return opts
|
||||
}
|
||||
return o.SelectOrder(order)
|
||||
}
|
||||
|
||||
// SelectOrder returns a slice of options ordered and selected by a byte array
|
||||
// usually defined by OptionParameterRequestList. This result is expected to be
|
||||
// used in ReplyPacket()'s []Option parameter.
|
||||
func (o Options) SelectOrder(order []byte) []Option {
|
||||
opts := make([]Option, 0, len(order))
|
||||
for _, v := range order {
|
||||
if data, ok := o[OptionCode(v)]; ok {
|
||||
opts = append(opts, Option{Code: OptionCode(v), Value: data})
|
||||
}
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
type OpCode byte
|
||||
type MessageType byte // Option 53
|
150
vendor/github.com/d2g/dhcp4/packet.go
generated
vendored
150
vendor/github.com/d2g/dhcp4/packet.go
generated
vendored
@ -1,150 +0,0 @@
|
||||
package dhcp4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A DHCP packet
|
||||
type Packet []byte
|
||||
|
||||
func (p Packet) OpCode() OpCode { return OpCode(p[0]) }
|
||||
func (p Packet) HType() byte { return p[1] }
|
||||
func (p Packet) HLen() byte { return p[2] }
|
||||
func (p Packet) Hops() byte { return p[3] }
|
||||
func (p Packet) XId() []byte { return p[4:8] }
|
||||
func (p Packet) Secs() []byte { return p[8:10] } // Never Used?
|
||||
func (p Packet) Flags() []byte { return p[10:12] }
|
||||
func (p Packet) CIAddr() net.IP { return net.IP(p[12:16]) }
|
||||
func (p Packet) YIAddr() net.IP { return net.IP(p[16:20]) }
|
||||
func (p Packet) SIAddr() net.IP { return net.IP(p[20:24]) }
|
||||
func (p Packet) GIAddr() net.IP { return net.IP(p[24:28]) }
|
||||
func (p Packet) CHAddr() net.HardwareAddr {
|
||||
hLen := p.HLen()
|
||||
if hLen > 16 { // Prevent chaddr exceeding p boundary
|
||||
hLen = 16
|
||||
}
|
||||
return net.HardwareAddr(p[28 : 28+hLen]) // max endPos 44
|
||||
}
|
||||
|
||||
// 192 bytes of zeros BOOTP legacy
|
||||
func (p Packet) Cookie() []byte { return p[236:240] }
|
||||
func (p Packet) Options() []byte {
|
||||
if len(p) > 240 {
|
||||
return p[240:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Packet) Broadcast() bool { return p.Flags()[0] > 127 }
|
||||
|
||||
func (p Packet) SetBroadcast(broadcast bool) {
|
||||
if p.Broadcast() != broadcast {
|
||||
p.Flags()[0] ^= 128
|
||||
}
|
||||
}
|
||||
|
||||
func (p Packet) SetOpCode(c OpCode) { p[0] = byte(c) }
|
||||
func (p Packet) SetCHAddr(a net.HardwareAddr) {
|
||||
copy(p[28:44], a)
|
||||
p[2] = byte(len(a))
|
||||
}
|
||||
func (p Packet) SetHType(hType byte) { p[1] = hType }
|
||||
func (p Packet) SetCookie(cookie []byte) { copy(p.Cookie(), cookie) }
|
||||
func (p Packet) SetHops(hops byte) { p[3] = hops }
|
||||
func (p Packet) SetXId(xId []byte) { copy(p.XId(), xId) }
|
||||
func (p Packet) SetSecs(secs []byte) { copy(p.Secs(), secs) }
|
||||
func (p Packet) SetFlags(flags []byte) { copy(p.Flags(), flags) }
|
||||
func (p Packet) SetCIAddr(ip net.IP) { copy(p.CIAddr(), ip.To4()) }
|
||||
func (p Packet) SetYIAddr(ip net.IP) { copy(p.YIAddr(), ip.To4()) }
|
||||
func (p Packet) SetSIAddr(ip net.IP) { copy(p.SIAddr(), ip.To4()) }
|
||||
func (p Packet) SetGIAddr(ip net.IP) { copy(p.GIAddr(), ip.To4()) }
|
||||
|
||||
// Parses the packet's options into an Options map
|
||||
func (p Packet) ParseOptions() Options {
|
||||
opts := p.Options()
|
||||
options := make(Options, 10)
|
||||
for len(opts) >= 2 && OptionCode(opts[0]) != End {
|
||||
if OptionCode(opts[0]) == Pad {
|
||||
opts = opts[1:]
|
||||
continue
|
||||
}
|
||||
size := int(opts[1])
|
||||
if len(opts) < 2+size {
|
||||
break
|
||||
}
|
||||
options[OptionCode(opts[0])] = opts[2 : 2+size]
|
||||
opts = opts[2+size:]
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func NewPacket(opCode OpCode) Packet {
|
||||
p := make(Packet, 241)
|
||||
p.SetOpCode(opCode)
|
||||
p.SetHType(1) // Ethernet
|
||||
p.SetCookie([]byte{99, 130, 83, 99})
|
||||
p[240] = byte(End)
|
||||
return p
|
||||
}
|
||||
|
||||
// Appends a DHCP option to the end of a packet
|
||||
func (p *Packet) AddOption(o OptionCode, value []byte) {
|
||||
*p = append((*p)[:len(*p)-1], []byte{byte(o), byte(len(value))}...) // Strip off End, Add OptionCode and Length
|
||||
*p = append(*p, value...) // Add Option Value
|
||||
*p = append(*p, byte(End)) // Add on new End
|
||||
}
|
||||
|
||||
// Removes all options from packet.
|
||||
func (p *Packet) StripOptions() {
|
||||
*p = append((*p)[:240], byte(End))
|
||||
}
|
||||
|
||||
// Creates a request packet that a Client would send to a server.
|
||||
func RequestPacket(mt MessageType, chAddr net.HardwareAddr, cIAddr net.IP, xId []byte, broadcast bool, options []Option) Packet {
|
||||
p := NewPacket(BootRequest)
|
||||
p.SetCHAddr(chAddr)
|
||||
p.SetXId(xId)
|
||||
if cIAddr != nil {
|
||||
p.SetCIAddr(cIAddr)
|
||||
}
|
||||
p.SetBroadcast(broadcast)
|
||||
p.AddOption(OptionDHCPMessageType, []byte{byte(mt)})
|
||||
for _, o := range options {
|
||||
p.AddOption(o.Code, o.Value)
|
||||
}
|
||||
p.PadToMinSize()
|
||||
return p
|
||||
}
|
||||
|
||||
// ReplyPacket creates a reply packet that a Server would send to a client.
|
||||
// It uses the req Packet param to copy across common/necessary fields to
|
||||
// associate the reply the request.
|
||||
func ReplyPacket(req Packet, mt MessageType, serverId, yIAddr net.IP, leaseDuration time.Duration, options []Option) Packet {
|
||||
p := NewPacket(BootReply)
|
||||
p.SetXId(req.XId())
|
||||
p.SetFlags(req.Flags())
|
||||
p.SetYIAddr(yIAddr)
|
||||
p.SetGIAddr(req.GIAddr())
|
||||
p.SetCHAddr(req.CHAddr())
|
||||
p.AddOption(OptionDHCPMessageType, []byte{byte(mt)})
|
||||
p.AddOption(OptionServerIdentifier, []byte(serverId))
|
||||
if leaseDuration > 0 {
|
||||
p.AddOption(OptionIPAddressLeaseTime, OptionsLeaseTime(leaseDuration))
|
||||
}
|
||||
for _, o := range options {
|
||||
p.AddOption(o.Code, o.Value)
|
||||
}
|
||||
p.PadToMinSize()
|
||||
return p
|
||||
}
|
||||
|
||||
// PadToMinSize pads a packet so that when sent over UDP, the entire packet,
|
||||
// is 300 bytes (BOOTP min), to be compatible with really old devices.
|
||||
var padder [272]byte
|
||||
|
||||
func (p *Packet) PadToMinSize() {
|
||||
if n := len(*p); n < 272 {
|
||||
*p = append(*p, padder[:272-n]...)
|
||||
}
|
||||
}
|
354
vendor/github.com/d2g/dhcp4client/LICENSE
generated
vendored
354
vendor/github.com/d2g/dhcp4client/LICENSE
generated
vendored
@ -1,354 +0,0 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. “Contributor”
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. “Contributor Version”
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor’s Contribution.
|
||||
|
||||
1.3. “Contribution”
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. “Covered Software”
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. “Incompatible With Secondary Licenses”
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of version
|
||||
1.1 or earlier of the License, but not also under the terms of a
|
||||
Secondary License.
|
||||
|
||||
1.6. “Executable Form”
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. “Larger Work”
|
||||
|
||||
means a work that combines Covered Software with other material, in a separate
|
||||
file or files, that is not Covered Software.
|
||||
|
||||
1.8. “License”
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. “Licensable”
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether at the
|
||||
time of the initial grant or subsequently, any and all of the rights conveyed by
|
||||
this License.
|
||||
|
||||
1.10. “Modifications”
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to, deletion
|
||||
from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. “Patent Claims” of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method, process,
|
||||
and apparatus claims, in any patent Licensable by such Contributor that
|
||||
would be infringed, but for the grant of the License, by the making,
|
||||
using, selling, offering for sale, having made, import, or transfer of
|
||||
either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. “Secondary License”
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. “Source Code Form”
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. “You” (or “Your”)
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, “control” means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or as
|
||||
part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its Contributions
|
||||
or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution become
|
||||
effective for each Contribution on the date the Contributor first distributes
|
||||
such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under this
|
||||
License. No additional rights or licenses will be implied from the distribution
|
||||
or licensing of Covered Software under this License. Notwithstanding Section
|
||||
2.1(b) above, no patent license is granted by a Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party’s
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of its
|
||||
Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks, or
|
||||
logos of any Contributor (except as may be necessary to comply with the
|
||||
notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this License
|
||||
(see Section 10.2) or under the terms of a Secondary License (if permitted
|
||||
under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its Contributions
|
||||
are its original creation(s) or it has sufficient rights to grant the
|
||||
rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under applicable
|
||||
copyright doctrines of fair use, fair dealing, or other equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under the
|
||||
terms of this License. You must inform recipients that the Source Code Form
|
||||
of the Covered Software is governed by the terms of this License, and how
|
||||
they can obtain a copy of this License. You may not attempt to alter or
|
||||
restrict the recipients’ rights in the Source Code Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this License,
|
||||
or sublicense it under different terms, provided that the license for
|
||||
the Executable Form does not attempt to limit or alter the recipients’
|
||||
rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for the
|
||||
Covered Software. If the Larger Work is a combination of Covered Software
|
||||
with a work governed by one or more Secondary Licenses, and the Covered
|
||||
Software is not Incompatible With Secondary Licenses, this License permits
|
||||
You to additionally distribute such Covered Software under the terms of
|
||||
such Secondary License(s), so that the recipient of the Larger Work may, at
|
||||
their option, further distribute the Covered Software under the terms of
|
||||
either this License or such Secondary License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices (including
|
||||
copyright notices, patent notices, disclaimers of warranty, or limitations
|
||||
of liability) contained within the Source Code Form of the Covered
|
||||
Software, except that You may alter any license notices to the extent
|
||||
required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on behalf
|
||||
of any Contributor. You must make it absolutely clear that any such
|
||||
warranty, support, indemnity, or liability obligation is offered by You
|
||||
alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute, judicial
|
||||
order, or regulation then You must: (a) comply with the terms of this License
|
||||
to the maximum extent possible; and (b) describe the limitations and the code
|
||||
they affect. Such description must be placed in a text file included with all
|
||||
distributions of the Covered Software under this License. Except to the
|
||||
extent prohibited by statute or regulation, such description must be
|
||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
|
||||
if such Contributor fails to notify You of the non-compliance by some
|
||||
reasonable means prior to 60 days after You have come back into compliance.
|
||||
Moreover, Your grants from a particular Contributor are reinstated on an
|
||||
ongoing basis if such Contributor notifies You of the non-compliance by
|
||||
some reasonable means, this is the first time You have received notice of
|
||||
non-compliance with this License from such Contributor, and You become
|
||||
compliant prior to 30 days after Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions, counter-claims,
|
||||
and cross-claims) alleging that a Contributor Version directly or
|
||||
indirectly infringes any patent, then the rights granted to You by any and
|
||||
all Contributors for the Covered Software under Section 2.1 of this License
|
||||
shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an “as is” basis, without
|
||||
warranty of any kind, either expressed, implied, or statutory, including,
|
||||
without limitation, warranties that the Covered Software is free of defects,
|
||||
merchantable, fit for a particular purpose or non-infringing. The entire
|
||||
risk as to the quality and performance of the Covered Software is with You.
|
||||
Should any Covered Software prove defective in any respect, You (not any
|
||||
Contributor) assume the cost of any necessary servicing, repair, or
|
||||
correction. This disclaimer of warranty constitutes an essential part of this
|
||||
License. No use of any Covered Software is authorized under this License
|
||||
except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from such
|
||||
party’s negligence to the extent applicable law prohibits such limitation.
|
||||
Some jurisdictions do not allow the exclusion or limitation of incidental or
|
||||
consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts of
|
||||
a jurisdiction where the defendant maintains its principal place of business
|
||||
and such litigation shall be governed by laws of that jurisdiction, without
|
||||
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||
prevent a party’s ability to bring cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject matter
|
||||
hereof. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it
|
||||
enforceable. Any law or regulation which provides that the language of a
|
||||
contract shall be construed against the drafter shall not be used to construe
|
||||
this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version of
|
||||
the License under which You originally received the Covered Software, or
|
||||
under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a modified
|
||||
version of this License if you rename the license and remove any
|
||||
references to the name of the license steward (except to note that such
|
||||
modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file, then
|
||||
You may include the notice in a location (such as a LICENSE file in a relevant
|
||||
directory) where a recipient would be likely to look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is “Incompatible
|
||||
With Secondary Licenses”, as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
8
vendor/github.com/d2g/dhcp4client/README.md
generated
vendored
8
vendor/github.com/d2g/dhcp4client/README.md
generated
vendored
@ -1,8 +0,0 @@
|
||||
dhcp4client [](http://godoc.org/github.com/d2g/dhcp4client) [](https://coveralls.io/r/d2g/dhcp4client?branch=HEAD) [](https://codeship.com/projects/70187)
|
||||
===========
|
||||
|
||||
DHCP Client
|
||||
|
||||
|
||||
###### Thanks to:
|
||||
@eyakubovich For AF_PACKET support.
|
416
vendor/github.com/d2g/dhcp4client/client.go
generated
vendored
416
vendor/github.com/d2g/dhcp4client/client.go
generated
vendored
@ -1,416 +0,0 @@
|
||||
package dhcp4client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/d2g/dhcp4"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxDHCPLen = 576
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
hardwareAddr net.HardwareAddr //The HardwareAddr to send in the request.
|
||||
ignoreServers []net.IP //List of Servers to Ignore requests from.
|
||||
timeout time.Duration //Time before we timeout.
|
||||
broadcast bool //Set the Bcast flag in BOOTP Flags
|
||||
connection ConnectionInt //The Connection Method to use
|
||||
generateXID func([]byte) //Function Used to Generate a XID
|
||||
}
|
||||
|
||||
//Abstracts the type of underlying socket used
|
||||
type ConnectionInt interface {
|
||||
Close() error
|
||||
Write(packet []byte) error
|
||||
ReadFrom() ([]byte, net.IP, error)
|
||||
SetReadTimeout(t time.Duration) error
|
||||
}
|
||||
|
||||
func New(options ...func(*Client) error) (*Client, error) {
|
||||
c := Client{
|
||||
timeout: time.Second * 10,
|
||||
broadcast: true,
|
||||
}
|
||||
|
||||
err := c.SetOption(options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.generateXID == nil {
|
||||
// https://tools.ietf.org/html/rfc2131#section-4.1 explains:
|
||||
//
|
||||
// A DHCP client MUST choose 'xid's in such a way as to minimize the chance
|
||||
// of using an 'xid' identical to one used by another client.
|
||||
//
|
||||
// Hence, seed a random number generator with the current time and hardware
|
||||
// address.
|
||||
h := fnv.New64()
|
||||
h.Write(c.hardwareAddr)
|
||||
seed := int64(h.Sum64()) + time.Now().Unix()
|
||||
rnd := rand.New(rand.NewSource(seed))
|
||||
var rndMu sync.Mutex
|
||||
c.generateXID = func(b []byte) {
|
||||
rndMu.Lock()
|
||||
defer rndMu.Unlock()
|
||||
rnd.Read(b)
|
||||
}
|
||||
}
|
||||
|
||||
//if connection hasn't been set as an option create the default.
|
||||
if c.connection == nil {
|
||||
conn, err := NewInetSock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.connection = conn
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetOption(options ...func(*Client) error) error {
|
||||
for _, opt := range options {
|
||||
if err := opt(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Timeout(t time.Duration) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.timeout = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func IgnoreServers(s []net.IP) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.ignoreServers = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func HardwareAddr(h net.HardwareAddr) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.hardwareAddr = h
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Broadcast(b bool) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.broadcast = b
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Connection(conn ConnectionInt) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.connection = conn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateXID(g func([]byte)) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.generateXID = g
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//Close Connections
|
||||
func (c *Client) Close() error {
|
||||
if c.connection != nil {
|
||||
return c.connection.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Send the Discovery Packet to the Broadcast Channel
|
||||
func (c *Client) SendDiscoverPacket() (dhcp4.Packet, error) {
|
||||
discoveryPacket := c.DiscoverPacket()
|
||||
discoveryPacket.PadToMinSize()
|
||||
|
||||
return discoveryPacket, c.SendPacket(discoveryPacket)
|
||||
}
|
||||
|
||||
// TimeoutError records a timeout when waiting for a DHCP packet.
|
||||
type TimeoutError struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func (te *TimeoutError) Error() string {
|
||||
return fmt.Sprintf("no DHCP packet received within %v", te.Timeout)
|
||||
}
|
||||
|
||||
//Retreive Offer...
|
||||
//Wait for the offer for a specific Discovery Packet.
|
||||
func (c *Client) GetOffer(discoverPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
timeout := c.timeout - time.Since(start)
|
||||
if timeout < 0 {
|
||||
return dhcp4.Packet{}, &TimeoutError{Timeout: c.timeout}
|
||||
}
|
||||
|
||||
c.connection.SetReadTimeout(timeout)
|
||||
readBuffer, source, err := c.connection.ReadFrom()
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||
return dhcp4.Packet{}, &TimeoutError{Timeout: c.timeout}
|
||||
}
|
||||
return dhcp4.Packet{}, err
|
||||
}
|
||||
|
||||
offerPacket := dhcp4.Packet(readBuffer)
|
||||
offerPacketOptions := offerPacket.ParseOptions()
|
||||
|
||||
// Ignore Servers in my Ignore list
|
||||
for _, ignoreServer := range c.ignoreServers {
|
||||
if source.Equal(ignoreServer) {
|
||||
continue
|
||||
}
|
||||
|
||||
if offerPacket.SIAddr().Equal(ignoreServer) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(offerPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || dhcp4.MessageType(offerPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.Offer || !bytes.Equal(discoverPacket.XId(), offerPacket.XId()) {
|
||||
continue
|
||||
}
|
||||
|
||||
return offerPacket, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Send Request Based On the offer Received.
|
||||
func (c *Client) SendRequest(offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
||||
requestPacket := c.RequestPacket(offerPacket)
|
||||
requestPacket.PadToMinSize()
|
||||
|
||||
return requestPacket, c.SendPacket(requestPacket)
|
||||
}
|
||||
|
||||
//Retreive Acknowledgement
|
||||
//Wait for the offer for a specific Request Packet.
|
||||
func (c *Client) GetAcknowledgement(requestPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
timeout := c.timeout - time.Since(start)
|
||||
if timeout < 0 {
|
||||
return dhcp4.Packet{}, &TimeoutError{Timeout: c.timeout}
|
||||
}
|
||||
|
||||
c.connection.SetReadTimeout(timeout)
|
||||
readBuffer, source, err := c.connection.ReadFrom()
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||
return dhcp4.Packet{}, &TimeoutError{Timeout: c.timeout}
|
||||
}
|
||||
return dhcp4.Packet{}, err
|
||||
}
|
||||
|
||||
acknowledgementPacket := dhcp4.Packet(readBuffer)
|
||||
acknowledgementPacketOptions := acknowledgementPacket.ParseOptions()
|
||||
|
||||
// Ignore Servers in my Ignore list
|
||||
for _, ignoreServer := range c.ignoreServers {
|
||||
if source.Equal(ignoreServer) {
|
||||
continue
|
||||
}
|
||||
|
||||
if acknowledgementPacket.SIAddr().Equal(ignoreServer) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !bytes.Equal(requestPacket.XId(), acknowledgementPacket.XId()) || len(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || (dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK && dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.NAK) {
|
||||
continue
|
||||
}
|
||||
|
||||
return acknowledgementPacket, nil
|
||||
}
|
||||
}
|
||||
|
||||
//Send Decline to the received acknowledgement.
|
||||
func (c *Client) SendDecline(acknowledgementPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
||||
declinePacket := c.DeclinePacket(acknowledgementPacket)
|
||||
declinePacket.PadToMinSize()
|
||||
|
||||
return declinePacket, c.SendPacket(declinePacket)
|
||||
}
|
||||
|
||||
//Send a DHCP Packet.
|
||||
func (c *Client) SendPacket(packet dhcp4.Packet) error {
|
||||
return c.connection.Write(packet)
|
||||
}
|
||||
|
||||
//Create Discover Packet
|
||||
func (c *Client) DiscoverPacket() dhcp4.Packet {
|
||||
messageid := make([]byte, 4)
|
||||
c.generateXID(messageid)
|
||||
|
||||
packet := dhcp4.NewPacket(dhcp4.BootRequest)
|
||||
packet.SetCHAddr(c.hardwareAddr)
|
||||
packet.SetXId(messageid)
|
||||
packet.SetBroadcast(c.broadcast)
|
||||
|
||||
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Discover)})
|
||||
//packet.PadToMinSize()
|
||||
return packet
|
||||
}
|
||||
|
||||
//Create Request Packet
|
||||
func (c *Client) RequestPacket(offerPacket *dhcp4.Packet) dhcp4.Packet {
|
||||
offerOptions := offerPacket.ParseOptions()
|
||||
|
||||
packet := dhcp4.NewPacket(dhcp4.BootRequest)
|
||||
packet.SetCHAddr(c.hardwareAddr)
|
||||
|
||||
packet.SetXId(offerPacket.XId())
|
||||
packet.SetCIAddr(offerPacket.CIAddr())
|
||||
packet.SetSIAddr(offerPacket.SIAddr())
|
||||
|
||||
packet.SetBroadcast(c.broadcast)
|
||||
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)})
|
||||
packet.AddOption(dhcp4.OptionRequestedIPAddress, (offerPacket.YIAddr()).To4())
|
||||
packet.AddOption(dhcp4.OptionServerIdentifier, offerOptions[dhcp4.OptionServerIdentifier])
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
//Create Request Packet For a Renew
|
||||
func (c *Client) RenewalRequestPacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
|
||||
messageid := make([]byte, 4)
|
||||
c.generateXID(messageid)
|
||||
|
||||
acknowledgementOptions := acknowledgement.ParseOptions()
|
||||
|
||||
packet := dhcp4.NewPacket(dhcp4.BootRequest)
|
||||
packet.SetCHAddr(acknowledgement.CHAddr())
|
||||
|
||||
packet.SetXId(messageid)
|
||||
packet.SetCIAddr(acknowledgement.YIAddr())
|
||||
packet.SetSIAddr(acknowledgement.SIAddr())
|
||||
|
||||
packet.SetBroadcast(c.broadcast)
|
||||
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)})
|
||||
packet.AddOption(dhcp4.OptionRequestedIPAddress, (acknowledgement.YIAddr()).To4())
|
||||
packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
//Create Release Packet For a Release
|
||||
func (c *Client) ReleasePacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
|
||||
messageid := make([]byte, 4)
|
||||
c.generateXID(messageid)
|
||||
|
||||
acknowledgementOptions := acknowledgement.ParseOptions()
|
||||
|
||||
packet := dhcp4.NewPacket(dhcp4.BootRequest)
|
||||
packet.SetCHAddr(acknowledgement.CHAddr())
|
||||
|
||||
packet.SetXId(messageid)
|
||||
packet.SetCIAddr(acknowledgement.YIAddr())
|
||||
|
||||
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Release)})
|
||||
packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
//Create Decline Packet
|
||||
func (c *Client) DeclinePacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
|
||||
messageid := make([]byte, 4)
|
||||
c.generateXID(messageid)
|
||||
|
||||
acknowledgementOptions := acknowledgement.ParseOptions()
|
||||
|
||||
packet := dhcp4.NewPacket(dhcp4.BootRequest)
|
||||
packet.SetCHAddr(acknowledgement.CHAddr())
|
||||
packet.SetXId(messageid)
|
||||
|
||||
packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Decline)})
|
||||
packet.AddOption(dhcp4.OptionRequestedIPAddress, (acknowledgement.YIAddr()).To4())
|
||||
packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
//Lets do a Full DHCP Request.
|
||||
func (c *Client) Request() (bool, dhcp4.Packet, error) {
|
||||
discoveryPacket, err := c.SendDiscoverPacket()
|
||||
if err != nil {
|
||||
return false, discoveryPacket, err
|
||||
}
|
||||
|
||||
offerPacket, err := c.GetOffer(&discoveryPacket)
|
||||
if err != nil {
|
||||
return false, offerPacket, err
|
||||
}
|
||||
|
||||
requestPacket, err := c.SendRequest(&offerPacket)
|
||||
if err != nil {
|
||||
return false, requestPacket, err
|
||||
}
|
||||
|
||||
acknowledgement, err := c.GetAcknowledgement(&requestPacket)
|
||||
if err != nil {
|
||||
return false, acknowledgement, err
|
||||
}
|
||||
|
||||
acknowledgementOptions := acknowledgement.ParseOptions()
|
||||
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
|
||||
return false, acknowledgement, nil
|
||||
}
|
||||
|
||||
return true, acknowledgement, nil
|
||||
}
|
||||
|
||||
//Renew a lease backed on the Acknowledgement Packet.
|
||||
//Returns Sucessfull, The AcknoledgementPacket, Any Errors
|
||||
func (c *Client) Renew(acknowledgement dhcp4.Packet) (bool, dhcp4.Packet, error) {
|
||||
renewRequest := c.RenewalRequestPacket(&acknowledgement)
|
||||
renewRequest.PadToMinSize()
|
||||
|
||||
err := c.SendPacket(renewRequest)
|
||||
if err != nil {
|
||||
return false, renewRequest, err
|
||||
}
|
||||
|
||||
newAcknowledgement, err := c.GetAcknowledgement(&renewRequest)
|
||||
if err != nil {
|
||||
return false, newAcknowledgement, err
|
||||
}
|
||||
|
||||
newAcknowledgementOptions := newAcknowledgement.ParseOptions()
|
||||
if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
|
||||
return false, newAcknowledgement, nil
|
||||
}
|
||||
|
||||
return true, newAcknowledgement, nil
|
||||
}
|
||||
|
||||
//Release a lease backed on the Acknowledgement Packet.
|
||||
//Returns Any Errors
|
||||
func (c *Client) Release(acknowledgement dhcp4.Packet) error {
|
||||
release := c.ReleasePacket(&acknowledgement)
|
||||
release.PadToMinSize()
|
||||
|
||||
return c.SendPacket(release)
|
||||
}
|
18
vendor/github.com/d2g/dhcp4client/generatexid.go
generated
vendored
18
vendor/github.com/d2g/dhcp4client/generatexid.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
package dhcp4client
|
||||
|
||||
import (
|
||||
cryptorand "crypto/rand"
|
||||
mathrand "math/rand"
|
||||
)
|
||||
|
||||
func CryptoGenerateXID(b []byte) {
|
||||
if _, err := cryptorand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func MathGenerateXID(b []byte) {
|
||||
if _, err := mathrand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
75
vendor/github.com/d2g/dhcp4client/inetsock.go
generated
vendored
75
vendor/github.com/d2g/dhcp4client/inetsock.go
generated
vendored
@ -1,75 +0,0 @@
|
||||
package dhcp4client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type inetSock struct {
|
||||
*net.UDPConn
|
||||
|
||||
laddr net.UDPAddr
|
||||
raddr net.UDPAddr
|
||||
}
|
||||
|
||||
func NewInetSock(options ...func(*inetSock) error) (*inetSock, error) {
|
||||
c := &inetSock{
|
||||
laddr: net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 68},
|
||||
raddr: net.UDPAddr{IP: net.IPv4bcast, Port: 67},
|
||||
}
|
||||
|
||||
err := c.setOption(options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP("udp4", &c.laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.UDPConn = conn
|
||||
return c, err
|
||||
}
|
||||
|
||||
func (c *inetSock) setOption(options ...func(*inetSock) error) error {
|
||||
for _, opt := range options {
|
||||
if err := opt(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetLocalAddr(l net.UDPAddr) func(*inetSock) error {
|
||||
return func(c *inetSock) error {
|
||||
c.laddr = l
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func SetRemoteAddr(r net.UDPAddr) func(*inetSock) error {
|
||||
return func(c *inetSock) error {
|
||||
c.raddr = r
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *inetSock) Write(packet []byte) error {
|
||||
_, err := c.WriteToUDP(packet, &c.raddr)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *inetSock) ReadFrom() ([]byte, net.IP, error) {
|
||||
readBuffer := make([]byte, MaxDHCPLen)
|
||||
n, source, err := c.ReadFromUDP(readBuffer)
|
||||
if source != nil {
|
||||
return readBuffer[:n], source.IP, err
|
||||
} else {
|
||||
return readBuffer[:n], net.IP{}, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *inetSock) SetReadTimeout(t time.Duration) error {
|
||||
return c.SetReadDeadline(time.Now().Add(t))
|
||||
}
|
147
vendor/github.com/d2g/dhcp4client/pktsock_linux.go
generated
vendored
147
vendor/github.com/d2g/dhcp4client/pktsock_linux.go
generated
vendored
@ -1,147 +0,0 @@
|
||||
package dhcp4client
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
minIPHdrLen = 20
|
||||
maxIPHdrLen = 60
|
||||
udpHdrLen = 8
|
||||
ip4Ver = 0x40
|
||||
ttl = 16
|
||||
srcPort = 68
|
||||
dstPort = 67
|
||||
)
|
||||
|
||||
var (
|
||||
bcastMAC = []byte{255, 255, 255, 255, 255, 255}
|
||||
)
|
||||
|
||||
// abstracts AF_PACKET
|
||||
type packetSock struct {
|
||||
fd int
|
||||
ifindex int
|
||||
}
|
||||
|
||||
func NewPacketSock(ifindex int) (*packetSock, error) {
|
||||
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, int(swap16(unix.ETH_P_IP)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr := unix.SockaddrLinklayer{
|
||||
Ifindex: ifindex,
|
||||
Protocol: swap16(unix.ETH_P_IP),
|
||||
}
|
||||
|
||||
if err = unix.Bind(fd, &addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &packetSock{
|
||||
fd: fd,
|
||||
ifindex: ifindex,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pc *packetSock) Close() error {
|
||||
return unix.Close(pc.fd)
|
||||
}
|
||||
|
||||
func (pc *packetSock) Write(packet []byte) error {
|
||||
lladdr := unix.SockaddrLinklayer{
|
||||
Ifindex: pc.ifindex,
|
||||
Protocol: swap16(unix.ETH_P_IP),
|
||||
Halen: uint8(len(bcastMAC)),
|
||||
}
|
||||
copy(lladdr.Addr[:], bcastMAC)
|
||||
|
||||
pkt := make([]byte, minIPHdrLen+udpHdrLen+len(packet))
|
||||
|
||||
fillIPHdr(pkt[0:minIPHdrLen], udpHdrLen+uint16(len(packet)))
|
||||
fillUDPHdr(pkt[minIPHdrLen:minIPHdrLen+udpHdrLen], uint16(len(packet)))
|
||||
|
||||
// payload
|
||||
copy(pkt[minIPHdrLen+udpHdrLen:len(pkt)], packet)
|
||||
|
||||
return unix.Sendto(pc.fd, pkt, 0, &lladdr)
|
||||
}
|
||||
|
||||
func (pc *packetSock) ReadFrom() ([]byte, net.IP, error) {
|
||||
pkt := make([]byte, maxIPHdrLen+udpHdrLen+MaxDHCPLen)
|
||||
n, _, err := unix.Recvfrom(pc.fd, pkt, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// IP hdr len
|
||||
ihl := int(pkt[0]&0x0F) * 4
|
||||
// Source IP address
|
||||
src := net.IP(pkt[12:16])
|
||||
|
||||
return pkt[ihl+udpHdrLen : n], src, nil
|
||||
}
|
||||
|
||||
func (pc *packetSock) SetReadTimeout(t time.Duration) error {
|
||||
|
||||
tv := unix.NsecToTimeval(t.Nanoseconds())
|
||||
return unix.SetsockoptTimeval(pc.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv)
|
||||
}
|
||||
|
||||
// compute's 1's complement checksum
|
||||
func chksum(p []byte, csum []byte) {
|
||||
cklen := len(p)
|
||||
s := uint32(0)
|
||||
for i := 0; i < (cklen - 1); i += 2 {
|
||||
s += uint32(p[i+1])<<8 | uint32(p[i])
|
||||
}
|
||||
if cklen&1 == 1 {
|
||||
s += uint32(p[cklen-1])
|
||||
}
|
||||
s = (s >> 16) + (s & 0xffff)
|
||||
s = s + (s >> 16)
|
||||
s = ^s
|
||||
|
||||
csum[0] = uint8(s & 0xff)
|
||||
csum[1] = uint8(s >> 8)
|
||||
}
|
||||
|
||||
func fillIPHdr(hdr []byte, payloadLen uint16) {
|
||||
// version + IHL
|
||||
hdr[0] = ip4Ver | (minIPHdrLen / 4)
|
||||
// total length
|
||||
binary.BigEndian.PutUint16(hdr[2:4], uint16(len(hdr))+payloadLen)
|
||||
// identification
|
||||
if _, err := rand.Read(hdr[4:5]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// TTL
|
||||
hdr[8] = 16
|
||||
// Protocol
|
||||
hdr[9] = unix.IPPROTO_UDP
|
||||
// dst IP
|
||||
copy(hdr[16:20], net.IPv4bcast.To4())
|
||||
// compute IP hdr checksum
|
||||
chksum(hdr[0:len(hdr)], hdr[10:12])
|
||||
}
|
||||
|
||||
func fillUDPHdr(hdr []byte, payloadLen uint16) {
|
||||
// src port
|
||||
binary.BigEndian.PutUint16(hdr[0:2], srcPort)
|
||||
// dest port
|
||||
binary.BigEndian.PutUint16(hdr[2:4], dstPort)
|
||||
// length
|
||||
binary.BigEndian.PutUint16(hdr[4:6], udpHdrLen+payloadLen)
|
||||
}
|
||||
|
||||
func swap16(x uint16) uint16 {
|
||||
var b [2]byte
|
||||
binary.BigEndian.PutUint16(b[:], x)
|
||||
return binary.LittleEndian.Uint16(b[:])
|
||||
}
|
354
vendor/github.com/d2g/dhcp4server/LICENSE
generated
vendored
354
vendor/github.com/d2g/dhcp4server/LICENSE
generated
vendored
@ -1,354 +0,0 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. “Contributor”
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. “Contributor Version”
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor’s Contribution.
|
||||
|
||||
1.3. “Contribution”
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. “Covered Software”
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. “Incompatible With Secondary Licenses”
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of version
|
||||
1.1 or earlier of the License, but not also under the terms of a
|
||||
Secondary License.
|
||||
|
||||
1.6. “Executable Form”
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. “Larger Work”
|
||||
|
||||
means a work that combines Covered Software with other material, in a separate
|
||||
file or files, that is not Covered Software.
|
||||
|
||||
1.8. “License”
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. “Licensable”
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether at the
|
||||
time of the initial grant or subsequently, any and all of the rights conveyed by
|
||||
this License.
|
||||
|
||||
1.10. “Modifications”
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to, deletion
|
||||
from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. “Patent Claims” of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method, process,
|
||||
and apparatus claims, in any patent Licensable by such Contributor that
|
||||
would be infringed, but for the grant of the License, by the making,
|
||||
using, selling, offering for sale, having made, import, or transfer of
|
||||
either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. “Secondary License”
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. “Source Code Form”
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. “You” (or “Your”)
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, “control” means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or as
|
||||
part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its Contributions
|
||||
or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution become
|
||||
effective for each Contribution on the date the Contributor first distributes
|
||||
such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under this
|
||||
License. No additional rights or licenses will be implied from the distribution
|
||||
or licensing of Covered Software under this License. Notwithstanding Section
|
||||
2.1(b) above, no patent license is granted by a Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party’s
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of its
|
||||
Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks, or
|
||||
logos of any Contributor (except as may be necessary to comply with the
|
||||
notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this License
|
||||
(see Section 10.2) or under the terms of a Secondary License (if permitted
|
||||
under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its Contributions
|
||||
are its original creation(s) or it has sufficient rights to grant the
|
||||
rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under applicable
|
||||
copyright doctrines of fair use, fair dealing, or other equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under the
|
||||
terms of this License. You must inform recipients that the Source Code Form
|
||||
of the Covered Software is governed by the terms of this License, and how
|
||||
they can obtain a copy of this License. You may not attempt to alter or
|
||||
restrict the recipients’ rights in the Source Code Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this License,
|
||||
or sublicense it under different terms, provided that the license for
|
||||
the Executable Form does not attempt to limit or alter the recipients’
|
||||
rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for the
|
||||
Covered Software. If the Larger Work is a combination of Covered Software
|
||||
with a work governed by one or more Secondary Licenses, and the Covered
|
||||
Software is not Incompatible With Secondary Licenses, this License permits
|
||||
You to additionally distribute such Covered Software under the terms of
|
||||
such Secondary License(s), so that the recipient of the Larger Work may, at
|
||||
their option, further distribute the Covered Software under the terms of
|
||||
either this License or such Secondary License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices (including
|
||||
copyright notices, patent notices, disclaimers of warranty, or limitations
|
||||
of liability) contained within the Source Code Form of the Covered
|
||||
Software, except that You may alter any license notices to the extent
|
||||
required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on behalf
|
||||
of any Contributor. You must make it absolutely clear that any such
|
||||
warranty, support, indemnity, or liability obligation is offered by You
|
||||
alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute, judicial
|
||||
order, or regulation then You must: (a) comply with the terms of this License
|
||||
to the maximum extent possible; and (b) describe the limitations and the code
|
||||
they affect. Such description must be placed in a text file included with all
|
||||
distributions of the Covered Software under this License. Except to the
|
||||
extent prohibited by statute or regulation, such description must be
|
||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
|
||||
if such Contributor fails to notify You of the non-compliance by some
|
||||
reasonable means prior to 60 days after You have come back into compliance.
|
||||
Moreover, Your grants from a particular Contributor are reinstated on an
|
||||
ongoing basis if such Contributor notifies You of the non-compliance by
|
||||
some reasonable means, this is the first time You have received notice of
|
||||
non-compliance with this License from such Contributor, and You become
|
||||
compliant prior to 30 days after Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions, counter-claims,
|
||||
and cross-claims) alleging that a Contributor Version directly or
|
||||
indirectly infringes any patent, then the rights granted to You by any and
|
||||
all Contributors for the Covered Software under Section 2.1 of this License
|
||||
shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an “as is” basis, without
|
||||
warranty of any kind, either expressed, implied, or statutory, including,
|
||||
without limitation, warranties that the Covered Software is free of defects,
|
||||
merchantable, fit for a particular purpose or non-infringing. The entire
|
||||
risk as to the quality and performance of the Covered Software is with You.
|
||||
Should any Covered Software prove defective in any respect, You (not any
|
||||
Contributor) assume the cost of any necessary servicing, repair, or
|
||||
correction. This disclaimer of warranty constitutes an essential part of this
|
||||
License. No use of any Covered Software is authorized under this License
|
||||
except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from such
|
||||
party’s negligence to the extent applicable law prohibits such limitation.
|
||||
Some jurisdictions do not allow the exclusion or limitation of incidental or
|
||||
consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts of
|
||||
a jurisdiction where the defendant maintains its principal place of business
|
||||
and such litigation shall be governed by laws of that jurisdiction, without
|
||||
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||
prevent a party’s ability to bring cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject matter
|
||||
hereof. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it
|
||||
enforceable. Any law or regulation which provides that the language of a
|
||||
contract shall be construed against the drafter shall not be used to construe
|
||||
this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version of
|
||||
the License under which You originally received the Covered Software, or
|
||||
under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a modified
|
||||
version of this License if you rename the license and remove any
|
||||
references to the name of the license steward (except to note that such
|
||||
modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file, then
|
||||
You may include the notice in a location (such as a LICENSE file in a relevant
|
||||
directory) where a recipient would be likely to look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is “Incompatible
|
||||
With Secondary Licenses”, as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
4
vendor/github.com/d2g/dhcp4server/README.md
generated
vendored
4
vendor/github.com/d2g/dhcp4server/README.md
generated
vendored
@ -1,4 +0,0 @@
|
||||
dhcp4server [](http://godoc.org/github.com/d2g/dhcp4server) [](https://coveralls.io/r/d2g/dhcp4server) [](https://codeship.com/projects/59804)
|
||||
===========
|
||||
|
||||
DHCP Server
|
102
vendor/github.com/d2g/dhcp4server/leasepool/lease.go
generated
vendored
102
vendor/github.com/d2g/dhcp4server/leasepool/lease.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
package leasepool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LeaseStatus int
|
||||
|
||||
const (
|
||||
Free LeaseStatus = 0
|
||||
Reserved LeaseStatus = 1
|
||||
Active LeaseStatus = 2
|
||||
)
|
||||
|
||||
type Lease struct {
|
||||
IP net.IP //The IP of the Lease
|
||||
Status LeaseStatus //Are Reserved, Active or Free
|
||||
MACAddress net.HardwareAddr //Mac Address of the Device
|
||||
ClientID []byte //ClientID of the request
|
||||
Hostname string //Hostname From option 12
|
||||
Expiry time.Time //Expiry Time
|
||||
}
|
||||
|
||||
//leaseMarshal is a mirror of Lease used for marshalling, since
|
||||
//net.HardwareAddr has no native marshalling capability.
|
||||
type leaseMarshal struct {
|
||||
IP string
|
||||
Status int
|
||||
MACAddress string
|
||||
ClientID string
|
||||
Hostname string
|
||||
Expiry time.Time
|
||||
}
|
||||
|
||||
func (this Lease) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(leaseMarshal{
|
||||
IP: this.IP.String(),
|
||||
Status: int(this.Status),
|
||||
MACAddress: this.MACAddress.String(),
|
||||
ClientID: hex.EncodeToString(this.ClientID),
|
||||
Hostname: this.Hostname,
|
||||
Expiry: this.Expiry,
|
||||
})
|
||||
}
|
||||
|
||||
func (this *Lease) UnmarshalJSON(data []byte) error {
|
||||
stringUnMarshal := leaseMarshal{}
|
||||
err := json.Unmarshal(data, &stringUnMarshal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
this.IP = net.ParseIP(stringUnMarshal.IP)
|
||||
this.Status = LeaseStatus(stringUnMarshal.Status)
|
||||
if stringUnMarshal.MACAddress != "" {
|
||||
this.MACAddress, err = net.ParseMAC(stringUnMarshal.MACAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing MAC address: %v", err)
|
||||
}
|
||||
}
|
||||
this.ClientID, err = hex.DecodeString(stringUnMarshal.ClientID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding clientID: %v", err)
|
||||
}
|
||||
this.Hostname = stringUnMarshal.Hostname
|
||||
this.Expiry = stringUnMarshal.Expiry
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this Lease) Equal(other Lease) bool {
|
||||
if !this.IP.Equal(other.IP) {
|
||||
return false
|
||||
}
|
||||
|
||||
if int(this.Status) != int(other.Status) {
|
||||
return false
|
||||
}
|
||||
|
||||
if this.MACAddress.String() != other.MACAddress.String() {
|
||||
return false
|
||||
}
|
||||
|
||||
if !bytes.Equal(this.ClientID, other.ClientID) {
|
||||
return false
|
||||
}
|
||||
|
||||
if this.Hostname != other.Hostname {
|
||||
return false
|
||||
}
|
||||
|
||||
if !this.Expiry.Equal(other.Expiry) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
49
vendor/github.com/d2g/dhcp4server/leasepool/leasepool.go
generated
vendored
49
vendor/github.com/d2g/dhcp4server/leasepool/leasepool.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
package leasepool
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
/*
|
||||
* Lease.IP is the Key.
|
||||
*/
|
||||
type LeasePool interface {
|
||||
//Add A Lease To The Pool
|
||||
AddLease(Lease) error
|
||||
|
||||
//Remove
|
||||
RemoveLease(net.IP) error
|
||||
|
||||
//Remove All Leases from the Pool (Required for Persistant LeaseManagers)
|
||||
PurgeLeases() error
|
||||
|
||||
/*
|
||||
* Get the Lease
|
||||
* -Found
|
||||
* -Copy Of the Lease
|
||||
* -Any Error
|
||||
*/
|
||||
GetLease(net.IP) (bool, Lease, error)
|
||||
|
||||
//Get the lease already in use by that hardware address and/or client identifier.
|
||||
GetLeaseForClient(net.HardwareAddr, []byte) (bool, Lease, error)
|
||||
|
||||
/*
|
||||
* -Lease Available
|
||||
* -Lease
|
||||
* -Error
|
||||
*/
|
||||
GetNextFreeLease() (bool, Lease, error)
|
||||
|
||||
/*
|
||||
* Return All Leases
|
||||
*/
|
||||
GetLeases() ([]Lease, error)
|
||||
|
||||
/*
|
||||
* Update Lease
|
||||
* - Has Updated
|
||||
* - Error
|
||||
*/
|
||||
UpdateLease(Lease) (bool, error)
|
||||
}
|
161
vendor/github.com/d2g/dhcp4server/leasepool/memorypool/memorypool.go
generated
vendored
161
vendor/github.com/d2g/dhcp4server/leasepool/memorypool/memorypool.go
generated
vendored
@ -1,161 +0,0 @@
|
||||
package memorypool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/d2g/dhcp4server/leasepool"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type MemoryPool struct {
|
||||
pool []leasepool.Lease
|
||||
poolLock sync.Mutex
|
||||
}
|
||||
|
||||
//Add A Lease To The Pool
|
||||
func (t *MemoryPool) AddLease(newLease leasepool.Lease) error {
|
||||
t.poolLock.Lock()
|
||||
defer t.poolLock.Unlock()
|
||||
|
||||
if t.pool == nil {
|
||||
t.pool = make([]leasepool.Lease, 0)
|
||||
}
|
||||
|
||||
for i := range t.pool {
|
||||
if t.pool[i].IP.Equal(newLease.IP) {
|
||||
//Lease Already Exists In Pool
|
||||
return errors.New("Error: Lease IP \"" + newLease.IP.String() + "\" alreay exists in Pool")
|
||||
}
|
||||
}
|
||||
|
||||
t.pool = append([]leasepool.Lease{newLease}, t.pool...)
|
||||
return nil
|
||||
}
|
||||
|
||||
//Remove a Lease From The Pool
|
||||
func (t *MemoryPool) RemoveLease(leaseIP net.IP) error {
|
||||
t.poolLock.Lock()
|
||||
defer t.poolLock.Unlock()
|
||||
|
||||
for i := range t.pool {
|
||||
if t.pool[i].IP.Equal(leaseIP) {
|
||||
|
||||
//Move the Last Element to This Position.
|
||||
t.pool[i] = t.pool[len(t.pool)-1]
|
||||
|
||||
//Shortern the Pool By One.
|
||||
t.pool = t.pool[0:(len(t.pool) - 1)]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Error: Lease IP \"" + leaseIP.String() + "\" Is Not In The Pool")
|
||||
}
|
||||
|
||||
//Remove All Leases from the Pool (Required for Persistant LeaseManagers)
|
||||
func (t *MemoryPool) PurgeLeases() error {
|
||||
t.poolLock.Lock()
|
||||
defer t.poolLock.Unlock()
|
||||
|
||||
t.pool = nil
|
||||
t.pool = make([]leasepool.Lease, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the Lease
|
||||
* -Found
|
||||
* -Copy Of the Lease
|
||||
* -Any Error
|
||||
*/
|
||||
func (t *MemoryPool) GetLease(leaseIP net.IP) (bool, leasepool.Lease, error) {
|
||||
t.poolLock.Lock()
|
||||
defer t.poolLock.Unlock()
|
||||
|
||||
for i := range t.pool {
|
||||
if t.pool[i].IP.Equal(leaseIP) {
|
||||
return true, t.pool[i], nil
|
||||
}
|
||||
}
|
||||
return false, leasepool.Lease{}, nil
|
||||
}
|
||||
|
||||
func makeKey(macAddress net.HardwareAddr, clientID []byte) []byte {
|
||||
key := []byte(macAddress)
|
||||
if len(clientID) > 0 {
|
||||
key = append(key, clientID...)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
//Get the lease already in use by that hardware address and/or client identifier.
|
||||
func (t *MemoryPool) GetLeaseForClient(macAddress net.HardwareAddr, clientID []byte) (bool, leasepool.Lease, error) {
|
||||
t.poolLock.Lock()
|
||||
defer t.poolLock.Unlock()
|
||||
|
||||
needleKey := makeKey(macAddress, clientID)
|
||||
for i := range t.pool {
|
||||
haystackKey := makeKey(t.pool[i].MACAddress, t.pool[i].ClientID)
|
||||
if bytes.Equal(needleKey, haystackKey) {
|
||||
return true, t.pool[i], nil
|
||||
}
|
||||
}
|
||||
return false, leasepool.Lease{}, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* -Lease Available
|
||||
* -Lease
|
||||
* -Error
|
||||
*/
|
||||
func (t *MemoryPool) GetNextFreeLease() (bool, leasepool.Lease, error) {
|
||||
t.poolLock.Lock()
|
||||
defer t.poolLock.Unlock()
|
||||
|
||||
//Loop Through the elements backwards.
|
||||
for i := (len(t.pool) - 1); i >= 0; i-- {
|
||||
//If the Lease Is Free
|
||||
if t.pool[i].Status == leasepool.Free {
|
||||
//Take the Element
|
||||
iLease := t.pool[i]
|
||||
//Shrink the Pool By 1
|
||||
t.pool = t.pool[:(len(t.pool) - 1)]
|
||||
//Place the Lease At the Begining (This saves us having some sort of counter...)
|
||||
t.pool = append([]leasepool.Lease{iLease}, t.pool...)
|
||||
return true, iLease, nil
|
||||
}
|
||||
}
|
||||
return false, leasepool.Lease{}, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Return All Leases
|
||||
*/
|
||||
func (t *MemoryPool) GetLeases() ([]leasepool.Lease, error) {
|
||||
return t.pool, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Update Lease
|
||||
* - Has Updated
|
||||
* - Error
|
||||
*/
|
||||
func (t *MemoryPool) UpdateLease(lease leasepool.Lease) (bool, error) {
|
||||
t.poolLock.Lock()
|
||||
defer t.poolLock.Unlock()
|
||||
|
||||
for i := range t.pool {
|
||||
if t.pool[i].IP.Equal(lease.IP) {
|
||||
|
||||
t.pool[i].MACAddress = lease.MACAddress
|
||||
t.pool[i].ClientID = lease.ClientID
|
||||
t.pool[i].Hostname = lease.Hostname
|
||||
t.pool[i].Expiry = lease.Expiry
|
||||
t.pool[i].Status = lease.Status
|
||||
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
590
vendor/github.com/d2g/dhcp4server/server.go
generated
vendored
590
vendor/github.com/d2g/dhcp4server/server.go
generated
vendored
@ -1,590 +0,0 @@
|
||||
package dhcp4server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/d2g/dhcp4"
|
||||
"github.com/d2g/dhcp4server/leasepool"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
/*
|
||||
* The DHCP Server Structure
|
||||
*/
|
||||
type Server struct {
|
||||
//Configuration Options
|
||||
ip net.IP //The IP Address We Tell Clients The Server Is On.
|
||||
defaultGateway net.IP //The Default Gateway Address
|
||||
dnsServers []net.IP //DNS Servers
|
||||
subnetMask net.IP //ie. 255.255.255.0
|
||||
leaseDuration time.Duration //Number of Seconds
|
||||
ignoreIPs []net.IP //Slice of IP's that should be ignored by the Server.
|
||||
ignoreHardwareAddress []net.HardwareAddr //Slice of Hardware Addresses we should ignore.
|
||||
|
||||
//Local Address
|
||||
laddr net.UDPAddr
|
||||
|
||||
//Remote address
|
||||
raddr net.UDPAddr
|
||||
|
||||
//LeasePool
|
||||
leasePool leasepool.LeasePool //Lease Pool Manager
|
||||
|
||||
//Used to Gracefully Close the Server
|
||||
shutdown uint32
|
||||
//Listeners & Response Connection.
|
||||
connection *ipv4.PacketConn
|
||||
}
|
||||
|
||||
// Create A New Server
|
||||
func New(ip net.IP, l leasepool.LeasePool, options ...func(*Server) error) (*Server, error) {
|
||||
s := Server{
|
||||
ip: ip,
|
||||
defaultGateway: ip,
|
||||
dnsServers: []net.IP{net.IPv4(208, 67, 222, 222), net.IPv4(208, 67, 220, 220)}, //OPENDNS
|
||||
subnetMask: net.IPv4(255, 255, 255, 0),
|
||||
leaseDuration: 24 * time.Hour,
|
||||
leasePool: l,
|
||||
laddr: net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 67},
|
||||
raddr: net.UDPAddr{IP: net.IPv4bcast, Port: 68},
|
||||
}
|
||||
|
||||
err := s.setOptions(options...)
|
||||
if err != nil {
|
||||
return &s, err
|
||||
}
|
||||
|
||||
return &s, err
|
||||
}
|
||||
|
||||
func (s *Server) setOptions(options ...func(*Server) error) error {
|
||||
for _, opt := range options {
|
||||
if err := opt(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the Server IP
|
||||
func IP(i net.IP) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.ip = i
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the Default Gateway Address.
|
||||
func DefaultGateway(r net.IP) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.defaultGateway = r
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set the DNS servers.
|
||||
func DNSServers(dnss []net.IP) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.dnsServers = dnss
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Subnet Mask
|
||||
func SubnetMask(m net.IP) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.subnetMask = m
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set Lease Duration
|
||||
func LeaseDuration(d time.Duration) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.leaseDuration = d
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set Ignore IPs
|
||||
func IgnoreIPs(ips []net.IP) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.ignoreIPs = ips
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set Ignore Hardware Addresses
|
||||
func IgnoreHardwareAddresses(h []net.HardwareAddr) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.ignoreHardwareAddress = h
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set LeasePool
|
||||
func LeasePool(p leasepool.LeasePool) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.leasePool = p
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set The Local Address
|
||||
func SetLocalAddr(a net.UDPAddr) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.laddr = a
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set The Remote Address
|
||||
func SetRemoteAddr(a net.UDPAddr) func(*Server) error {
|
||||
return func(s *Server) error {
|
||||
s.raddr = a
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Start The DHCP Server
|
||||
*/
|
||||
func (s *Server) ListenAndServe() error {
|
||||
var err error
|
||||
|
||||
connection, err := net.ListenPacket("udp4", s.laddr.String())
|
||||
if err != nil {
|
||||
log.Printf("Debug: Error Returned From ListenPacket On \"%s\" Because of \"%s\"\n", s.laddr.String(), err.Error())
|
||||
return err
|
||||
}
|
||||
s.connection = ipv4.NewPacketConn(connection)
|
||||
defer s.connection.Close()
|
||||
|
||||
//We Currently Don't Use this Feature Which is the only bit that is Linux Only.
|
||||
//if err := s.connection.SetControlMessage(ipv4.FlagInterface, true); err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
log.Println("Trace: DHCP Server Listening.")
|
||||
|
||||
for {
|
||||
ListenForDHCPPackets:
|
||||
if s.shouldShutdown() {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Make Our Buffer (Max Buffer is 574) "I believe this 576 size comes from RFC 791" - Random Mailing list quote of the day.
|
||||
buffer := make([]byte, 576)
|
||||
|
||||
//Set Read Deadline
|
||||
s.connection.SetReadDeadline(time.Now().Add(time.Second))
|
||||
// Read Packet
|
||||
n, control_message, source, err := s.connection.ReadFrom(buffer)
|
||||
|
||||
if err != nil {
|
||||
|
||||
switch v := err.(type) {
|
||||
case *net.OpError:
|
||||
// If we've been signaled to shut down, ignore
|
||||
// the "use of closed network connection" error
|
||||
// since the connection was closed by the
|
||||
// shutdown request
|
||||
if s.shouldShutdown() {
|
||||
return nil
|
||||
}
|
||||
if v.Timeout() {
|
||||
goto ListenForDHCPPackets
|
||||
}
|
||||
case *net.AddrError:
|
||||
if v.Timeout() {
|
||||
goto ListenForDHCPPackets
|
||||
}
|
||||
case *net.UnknownNetworkError:
|
||||
if v.Timeout() {
|
||||
goto ListenForDHCPPackets
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Debug: Unexpect Error from Connection Read From: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
//We seem to have an issue with undersized packets?
|
||||
if n < 240 {
|
||||
log.Printf("Error: Invalid Packet Size \"%d\" Received:%v\n", n, buffer[:n])
|
||||
continue
|
||||
}
|
||||
|
||||
//We should ignore some requests
|
||||
//It shouldn't be possible to ignore IP's because they shouldn't have them as we're the DHCP server.
|
||||
//However, they can have i.e. if you're the client & server :S.
|
||||
for _, ipToIgnore := range s.ignoreIPs {
|
||||
if ipToIgnore.Equal(source.(*net.UDPAddr).IP) {
|
||||
log.Println("Debug: Ignoring DHCP Request From IP:" + ipToIgnore.String())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
packet := dhcp4.Packet(buffer[:n])
|
||||
|
||||
//We can ignore hardware addresses.
|
||||
//Usefull for ignoring a range of hardware addresses
|
||||
for _, hardwareAddressToIgnore := range s.ignoreHardwareAddress {
|
||||
if bytes.Equal(hardwareAddressToIgnore, packet.CHAddr()) {
|
||||
log.Println("Debug: Ignoring DHCP Request From Hardware Address:" + hardwareAddressToIgnore.String())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Trace: Packet Received ID:%v\n", packet.XId())
|
||||
log.Printf("Trace: Packet Options:%v\n", packet.ParseOptions())
|
||||
log.Printf("Trace: Packet Client IP : %v\n", packet.CIAddr().String())
|
||||
log.Printf("Trace: Packet Your IP : %v\n", packet.YIAddr().String())
|
||||
log.Printf("Trace: Packet Server IP : %v\n", packet.SIAddr().String())
|
||||
log.Printf("Trace: Packet Gateway IP: %v\n", packet.GIAddr().String())
|
||||
log.Printf("Trace: Packet Client Mac: %v\n", packet.CHAddr().String())
|
||||
|
||||
//We need to stop butting in with other servers.
|
||||
if packet.SIAddr().Equal(net.IPv4(0, 0, 0, 0)) || packet.SIAddr().Equal(net.IP{}) || packet.SIAddr().Equal(s.ip) {
|
||||
|
||||
returnPacket, err := s.ServeDHCP(packet)
|
||||
if err != nil {
|
||||
log.Println("Debug: Error Serving DHCP:" + err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if len(returnPacket) > 0 {
|
||||
log.Printf("Trace: Packet Returned ID:%v\n", returnPacket.XId())
|
||||
log.Printf("Trace: Packet Options:%v\n", returnPacket.ParseOptions())
|
||||
log.Printf("Trace: Packet Client IP : %v\n", returnPacket.CIAddr().String())
|
||||
log.Printf("Trace: Packet Your IP : %v\n", returnPacket.YIAddr().String())
|
||||
log.Printf("Trace: Packet Server IP : %v\n", returnPacket.SIAddr().String())
|
||||
log.Printf("Trace: Packet Gateway IP: %v\n", returnPacket.GIAddr().String())
|
||||
log.Printf("Trace: Packet Client Mac: %v\n", returnPacket.CHAddr().String())
|
||||
|
||||
_, err = s.connection.WriteTo(returnPacket, control_message, &s.raddr)
|
||||
if err != nil {
|
||||
log.Println("Debug: Error Writing:" + err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func getClientID(packetOptions dhcp4.Options) []byte {
|
||||
if clientID, ok := packetOptions[dhcp4.OptionClientIdentifier]; ok {
|
||||
return clientID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) ServeDHCP(packet dhcp4.Packet) (dhcp4.Packet, error) {
|
||||
packetOptions := packet.ParseOptions()
|
||||
|
||||
switch dhcp4.MessageType(packetOptions[dhcp4.OptionDHCPMessageType][0]) {
|
||||
case dhcp4.Discover:
|
||||
|
||||
//Discover Received from client
|
||||
//Lets get the lease we're going to send them
|
||||
found, lease, err := s.GetLease(packet)
|
||||
if err != nil {
|
||||
return dhcp4.Packet{}, err
|
||||
}
|
||||
|
||||
if !found {
|
||||
log.Println("Warning: It Looks Like Our Leases Are Depleted...")
|
||||
return dhcp4.Packet{}, nil
|
||||
}
|
||||
|
||||
offerPacket := s.OfferPacket(packet)
|
||||
offerPacket.SetYIAddr(lease.IP)
|
||||
|
||||
//Sort out the packet options
|
||||
offerPacket.PadToMinSize()
|
||||
|
||||
lease.Status = leasepool.Reserved
|
||||
lease.MACAddress = packet.CHAddr()
|
||||
lease.ClientID = getClientID(packetOptions)
|
||||
|
||||
//If the lease expires within the next 5 Mins increase the lease expiary (Giving the Client 5 mins to complete)
|
||||
if lease.Expiry.Before(time.Now().Add(time.Minute * 5)) {
|
||||
lease.Expiry = time.Now().Add(time.Minute * 5)
|
||||
}
|
||||
|
||||
if packetOptions[dhcp4.OptionHostName] != nil && string(packetOptions[dhcp4.OptionHostName]) != "" {
|
||||
lease.Hostname = string(packetOptions[dhcp4.OptionHostName])
|
||||
}
|
||||
|
||||
updated, err := s.leasePool.UpdateLease(lease)
|
||||
if err != nil {
|
||||
return dhcp4.Packet{}, err
|
||||
}
|
||||
|
||||
if !updated {
|
||||
//Unable to reserve lease (It's now active else where maybe?)
|
||||
return dhcp4.Packet{}, errors.New("Unable to Reserve Lease:" + lease.IP.String())
|
||||
}
|
||||
|
||||
return offerPacket, nil
|
||||
case dhcp4.Request:
|
||||
//Request Received from client
|
||||
//Lets get the lease we're going to send them
|
||||
found, lease, err := s.GetLease(packet)
|
||||
if err != nil {
|
||||
return dhcp4.Packet{}, err
|
||||
}
|
||||
|
||||
if !found {
|
||||
log.Println("Warning: It Looks Like Our Leases Are Depleted...")
|
||||
return dhcp4.Packet{}, nil
|
||||
}
|
||||
|
||||
//If the lease is not the one requested We should send a NAK..
|
||||
if len(packetOptions) > 0 && !net.IP(packetOptions[dhcp4.OptionRequestedIPAddress]).Equal(lease.IP) {
|
||||
//NAK
|
||||
declinePacket := s.DeclinePacket(packet)
|
||||
declinePacket.PadToMinSize()
|
||||
|
||||
return declinePacket, nil
|
||||
} else {
|
||||
lease.Status = leasepool.Active
|
||||
lease.MACAddress = packet.CHAddr()
|
||||
lease.ClientID = getClientID(packetOptions)
|
||||
|
||||
lease.Expiry = time.Now().Add(s.leaseDuration)
|
||||
|
||||
if packetOptions[dhcp4.OptionHostName] != nil && string(packetOptions[dhcp4.OptionHostName]) != "" {
|
||||
lease.Hostname = string(packetOptions[dhcp4.OptionHostName])
|
||||
}
|
||||
|
||||
updated, err := s.leasePool.UpdateLease(lease)
|
||||
if err != nil {
|
||||
return dhcp4.Packet{}, err
|
||||
}
|
||||
|
||||
if updated {
|
||||
//ACK
|
||||
acknowledgementPacket := s.AcknowledgementPacket(packet)
|
||||
acknowledgementPacket.SetYIAddr(lease.IP)
|
||||
|
||||
//Lease time.
|
||||
acknowledgementPacket.AddOption(dhcp4.OptionIPAddressLeaseTime, dhcp4.OptionsLeaseTime(lease.Expiry.Sub(time.Now())))
|
||||
acknowledgementPacket.PadToMinSize()
|
||||
|
||||
return acknowledgementPacket, nil
|
||||
} else {
|
||||
//NAK
|
||||
declinePacket := s.DeclinePacket(packet)
|
||||
declinePacket.PadToMinSize()
|
||||
|
||||
return declinePacket, nil
|
||||
}
|
||||
}
|
||||
case dhcp4.Decline:
|
||||
//Decline from the client:
|
||||
log.Printf("Debug: Decline Message:%v\n", packet)
|
||||
|
||||
case dhcp4.Release:
|
||||
//Decline from the client:
|
||||
log.Printf("Debug: Release Message:%v\n", packet)
|
||||
|
||||
default:
|
||||
log.Printf("Debug: Unexpected Packet Type:%v\n", dhcp4.MessageType(packetOptions[dhcp4.OptionDHCPMessageType][0]))
|
||||
}
|
||||
|
||||
return dhcp4.Packet{}, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Create DHCP Offer Packet
|
||||
*/
|
||||
func (s *Server) OfferPacket(discoverPacket dhcp4.Packet) dhcp4.Packet {
|
||||
|
||||
offerPacket := dhcp4.NewPacket(dhcp4.BootReply)
|
||||
offerPacket.SetXId(discoverPacket.XId())
|
||||
offerPacket.SetFlags(discoverPacket.Flags())
|
||||
|
||||
offerPacket.SetCHAddr(discoverPacket.CHAddr())
|
||||
offerPacket.SetGIAddr(discoverPacket.GIAddr())
|
||||
offerPacket.SetSecs(discoverPacket.Secs())
|
||||
|
||||
//53
|
||||
offerPacket.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Offer)})
|
||||
//54
|
||||
offerPacket.AddOption(dhcp4.OptionServerIdentifier, s.ip.To4())
|
||||
//51
|
||||
offerPacket.AddOption(dhcp4.OptionIPAddressLeaseTime, dhcp4.OptionsLeaseTime(s.leaseDuration))
|
||||
|
||||
//Other options go in requested order...
|
||||
discoverPacketOptions := discoverPacket.ParseOptions()
|
||||
|
||||
ourOptions := make(dhcp4.Options)
|
||||
|
||||
//1
|
||||
ourOptions[dhcp4.OptionSubnetMask] = s.subnetMask.To4()
|
||||
//3
|
||||
ourOptions[dhcp4.OptionRouter] = s.defaultGateway.To4()
|
||||
//6
|
||||
ourOptions[dhcp4.OptionDomainNameServer] = dhcp4.JoinIPs(s.dnsServers)
|
||||
|
||||
if discoverPacketOptions[dhcp4.OptionParameterRequestList] != nil {
|
||||
//Loop through the requested options and if we have them add them.
|
||||
for _, optionCode := range discoverPacketOptions[dhcp4.OptionParameterRequestList] {
|
||||
if !bytes.Equal(ourOptions[dhcp4.OptionCode(optionCode)], []byte{}) {
|
||||
offerPacket.AddOption(dhcp4.OptionCode(optionCode), ourOptions[dhcp4.OptionCode(optionCode)])
|
||||
delete(ourOptions, dhcp4.OptionCode(optionCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Add all the options not requested.
|
||||
for optionCode, optionValue := range ourOptions {
|
||||
offerPacket.AddOption(optionCode, optionValue)
|
||||
}
|
||||
|
||||
return offerPacket
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Create DHCP Acknowledgement
|
||||
*/
|
||||
func (s *Server) AcknowledgementPacket(requestPacket dhcp4.Packet) dhcp4.Packet {
|
||||
|
||||
acknowledgementPacket := dhcp4.NewPacket(dhcp4.BootReply)
|
||||
acknowledgementPacket.SetXId(requestPacket.XId())
|
||||
acknowledgementPacket.SetFlags(requestPacket.Flags())
|
||||
|
||||
acknowledgementPacket.SetGIAddr(requestPacket.GIAddr())
|
||||
acknowledgementPacket.SetCHAddr(requestPacket.CHAddr())
|
||||
acknowledgementPacket.SetSecs(requestPacket.Secs())
|
||||
|
||||
acknowledgementPacket.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.ACK)})
|
||||
acknowledgementPacket.AddOption(dhcp4.OptionSubnetMask, s.subnetMask.To4())
|
||||
acknowledgementPacket.AddOption(dhcp4.OptionRouter, s.defaultGateway.To4())
|
||||
acknowledgementPacket.AddOption(dhcp4.OptionDomainNameServer, dhcp4.JoinIPs(s.dnsServers))
|
||||
acknowledgementPacket.AddOption(dhcp4.OptionServerIdentifier, s.ip.To4())
|
||||
|
||||
return acknowledgementPacket
|
||||
}
|
||||
|
||||
/*
|
||||
* Create DHCP Decline
|
||||
*/
|
||||
func (s *Server) DeclinePacket(requestPacket dhcp4.Packet) dhcp4.Packet {
|
||||
|
||||
declinePacket := dhcp4.NewPacket(dhcp4.BootReply)
|
||||
declinePacket.SetXId(requestPacket.XId())
|
||||
declinePacket.SetFlags(requestPacket.Flags())
|
||||
|
||||
declinePacket.SetGIAddr(requestPacket.GIAddr())
|
||||
declinePacket.SetCHAddr(requestPacket.CHAddr())
|
||||
declinePacket.SetSecs(requestPacket.Secs())
|
||||
|
||||
declinePacket.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.NAK)})
|
||||
declinePacket.AddOption(dhcp4.OptionSubnetMask, s.subnetMask.To4())
|
||||
declinePacket.AddOption(dhcp4.OptionRouter, s.defaultGateway.To4())
|
||||
declinePacket.AddOption(dhcp4.OptionDomainNameServer, dhcp4.JoinIPs(s.dnsServers))
|
||||
declinePacket.AddOption(dhcp4.OptionServerIdentifier, s.ip.To4())
|
||||
|
||||
return declinePacket
|
||||
}
|
||||
|
||||
/*
|
||||
* Get Lease tries to work out the best lease for the packet supplied.
|
||||
* Taking into account all Requested IP, Exisitng MACAddresses and Free leases.
|
||||
*/
|
||||
func (s *Server) GetLease(packet dhcp4.Packet) (found bool, lease leasepool.Lease, err error) {
|
||||
packetOptions := packet.ParseOptions()
|
||||
|
||||
clientID := getClientID(packetOptions)
|
||||
|
||||
//Requested an IP
|
||||
if (len(packetOptions) > 0) &&
|
||||
packetOptions[dhcp4.OptionRequestedIPAddress] != nil &&
|
||||
!net.IP(packetOptions[dhcp4.OptionRequestedIPAddress]).Equal(net.IP{}) {
|
||||
//An IP Has Been Requested Let's Try and Get that One.
|
||||
|
||||
found, lease, err = s.leasePool.GetLease(net.IP(packetOptions[dhcp4.OptionRequestedIPAddress]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if found {
|
||||
//If lease is free, return it to client. If it is not
|
||||
//free match against the MAC address and client
|
||||
//identifier.
|
||||
if lease.Status == leasepool.Free {
|
||||
//Lease Is Free you Can Have it.
|
||||
return
|
||||
}
|
||||
if bytes.Equal(lease.MACAddress, packet.CHAddr()) &&
|
||||
bytes.Equal(lease.ClientID, clientID) {
|
||||
//Lease isn't free but it's yours
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Ok Even if you requested an IP you can't have it.
|
||||
found, lease, err = s.leasePool.GetLeaseForClient(packet.CHAddr(), clientID)
|
||||
if found || err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//Just get the next free lease if you can.
|
||||
found, lease, err = s.leasePool.GetNextFreeLease()
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown The Server Gracefully
|
||||
*/
|
||||
func (s *Server) Shutdown() {
|
||||
atomic.StoreUint32(&s.shutdown, 1)
|
||||
s.connection.Close()
|
||||
}
|
||||
|
||||
func (s *Server) shouldShutdown() bool {
|
||||
return atomic.LoadUint32(&s.shutdown) == 1
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage Collection
|
||||
* Run Garbage Collection On Your Leases To Free Expired Leases.
|
||||
*/
|
||||
func (s *Server) GC() error {
|
||||
leases, err := s.leasePool.GetLeases()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range leases {
|
||||
if leases[i].Status != leasepool.Free {
|
||||
//Lease Is Not Free
|
||||
|
||||
if time.Now().After(leases[i].Expiry) {
|
||||
//Lease has expired.
|
||||
leases[i].Status = leasepool.Free
|
||||
updated, err := s.leasePool.UpdateLease(leases[i])
|
||||
if err != nil {
|
||||
log.Printf("Warning: Error trying to Free Lease %s \"%v\"\n", leases[i].IP.To4().String(), err)
|
||||
}
|
||||
if !updated {
|
||||
log.Printf("Warning: Unable to Free Lease %s\n", leases[i].IP.To4().String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
10
vendor/github.com/insomniacslk/dhcp/CONTRIBUTORS.md
generated
vendored
Normal file
10
vendor/github.com/insomniacslk/dhcp/CONTRIBUTORS.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
## Contributors
|
||||
|
||||
* Andrea Barberio (main author)
|
||||
* Pablo Mazzini (tons of fixes and new options)
|
||||
* Sean Karlage (BSDP package, and of tons of improvements to the DHCPv4 package)
|
||||
* Owen Mooney (several option fixes and modifiers)
|
||||
* Mikolaj Walczak (asynchronous DHCPv6 client)
|
||||
* Chris Koch (tons of improvements in DHCPv4 and DHCPv6 internals and interface)
|
||||
* Akshay Navale, Brandon Bennett and Chris Gorham (ZTPv6 and ZTPv4 packages)
|
||||
* Anatole Denis (tons of fixes and new options)
|
29
vendor/github.com/insomniacslk/dhcp/LICENSE
generated
vendored
Normal file
29
vendor/github.com/insomniacslk/dhcp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, Andrea Barberio
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
10
vendor/github.com/insomniacslk/dhcp/dhcpv4/bindtointerface.go
generated
vendored
Normal file
10
vendor/github.com/insomniacslk/dhcp/dhcpv4/bindtointerface.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"github.com/insomniacslk/dhcp/interfaces"
|
||||
)
|
||||
|
||||
// BindToInterface (deprecated) redirects to interfaces.BindToInterface
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
return interfaces.BindToInterface(fd, ifname)
|
||||
}
|
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/defaults.go
generated
vendored
Normal file
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/defaults.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
package dhcpv4
|
||||
|
||||
const (
|
||||
ServerPort = 67
|
||||
ClientPort = 68
|
||||
)
|
856
vendor/github.com/insomniacslk/dhcp/dhcpv4/dhcpv4.go
generated
vendored
Normal file
856
vendor/github.com/insomniacslk/dhcp/dhcpv4/dhcpv4.go
generated
vendored
Normal file
@ -0,0 +1,856 @@
|
||||
// Package dhcpv4 provides encoding and decoding of DHCPv4 packets and options.
|
||||
//
|
||||
// Example Usage:
|
||||
//
|
||||
// p, err := dhcpv4.New(
|
||||
// dhcpv4.WithClientIP(net.IP{192, 168, 0, 1}),
|
||||
// dhcpv4.WithMessageType(dhcpv4.MessageTypeInform),
|
||||
// )
|
||||
// p.UpdateOption(dhcpv4.OptServerIdentifier(net.IP{192, 110, 110, 110}))
|
||||
//
|
||||
// // Retrieve the DHCP Message Type option.
|
||||
// m := p.MessageType()
|
||||
//
|
||||
// bytesOnTheWire := p.ToBytes()
|
||||
// longSummary := p.Summary()
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
"github.com/u-root/uio/rand"
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
const (
|
||||
// minPacketLen is the minimum DHCP header length.
|
||||
minPacketLen = 236
|
||||
|
||||
// MaxHWAddrLen is the maximum hardware address length of the ClientHWAddr
|
||||
// (client hardware address) according to RFC 2131, Section 2. This is the
|
||||
// link-layer destination a server must send responses to.
|
||||
MaxHWAddrLen = 16
|
||||
|
||||
// MaxMessageSize is the maximum size in bytes that a DHCPv4 packet can hold.
|
||||
MaxMessageSize = 576
|
||||
|
||||
// Per RFC 951, the minimum length of a packet is 300 bytes.
|
||||
bootpMinLen = 300
|
||||
)
|
||||
|
||||
// RandomTimeout is the amount of time to wait until random number generation
|
||||
// is canceled.
|
||||
var RandomTimeout = 2 * time.Minute
|
||||
|
||||
// magicCookie is the magic 4-byte value at the beginning of the list of options
|
||||
// in a DHCPv4 packet.
|
||||
var magicCookie = [4]byte{99, 130, 83, 99}
|
||||
|
||||
// DHCPv4 represents a DHCPv4 packet header and options. See the New* functions
|
||||
// to build DHCPv4 packets.
|
||||
type DHCPv4 struct {
|
||||
OpCode OpcodeType
|
||||
HWType iana.HWType
|
||||
HopCount uint8
|
||||
TransactionID TransactionID
|
||||
NumSeconds uint16
|
||||
Flags uint16
|
||||
ClientIPAddr net.IP
|
||||
YourIPAddr net.IP
|
||||
ServerIPAddr net.IP
|
||||
GatewayIPAddr net.IP
|
||||
ClientHWAddr net.HardwareAddr
|
||||
ServerHostName string
|
||||
BootFileName string
|
||||
Options Options
|
||||
}
|
||||
|
||||
// Modifier defines the signature for functions that can modify DHCPv4
|
||||
// structures. This is used to simplify packet manipulation
|
||||
type Modifier func(d *DHCPv4)
|
||||
|
||||
// IPv4AddrsForInterface obtains the currently-configured, non-loopback IPv4
|
||||
// addresses for iface.
|
||||
func IPv4AddrsForInterface(iface *net.Interface) ([]net.IP, error) {
|
||||
if iface == nil {
|
||||
return nil, errors.New("IPv4AddrsForInterface: iface cannot be nil")
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetExternalIPv4Addrs(addrs)
|
||||
}
|
||||
|
||||
// GetExternalIPv4Addrs obtains the currently-configured, non-loopback IPv4
|
||||
// addresses from `addrs` coming from a particular interface (e.g.
|
||||
// net.Interface.Addrs).
|
||||
func GetExternalIPv4Addrs(addrs []net.Addr) ([]net.IP, error) {
|
||||
var v4addrs []net.IP
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
v4addrs = append(v4addrs, ip)
|
||||
}
|
||||
return v4addrs, nil
|
||||
}
|
||||
|
||||
// GenerateTransactionID generates a random 32-bits number suitable for use as
|
||||
// TransactionID
|
||||
func GenerateTransactionID() (TransactionID, error) {
|
||||
var xid TransactionID
|
||||
ctx, cancel := context.WithTimeout(context.Background(), RandomTimeout)
|
||||
defer cancel()
|
||||
n, err := rand.ReadContext(ctx, xid[:])
|
||||
if err != nil {
|
||||
return xid, fmt.Errorf("could not get random number: %v", err)
|
||||
}
|
||||
if n != 4 {
|
||||
return xid, errors.New("invalid random sequence for transaction ID: smaller than 32 bits")
|
||||
}
|
||||
return xid, err
|
||||
}
|
||||
|
||||
// New creates a new DHCPv4 structure and fill it up with default values. It
|
||||
// won't be a valid DHCPv4 message so you will need to adjust its fields.
|
||||
// See also NewDiscovery, NewRequest, NewAcknowledge, NewInform and NewRelease.
|
||||
func New(modifiers ...Modifier) (*DHCPv4, error) {
|
||||
xid, err := GenerateTransactionID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := DHCPv4{
|
||||
OpCode: OpcodeBootRequest,
|
||||
HWType: iana.HWTypeEthernet,
|
||||
ClientHWAddr: make(net.HardwareAddr, 6),
|
||||
HopCount: 0,
|
||||
TransactionID: xid,
|
||||
NumSeconds: 0,
|
||||
Flags: 0,
|
||||
ClientIPAddr: net.IPv4zero,
|
||||
YourIPAddr: net.IPv4zero,
|
||||
ServerIPAddr: net.IPv4zero,
|
||||
GatewayIPAddr: net.IPv4zero,
|
||||
Options: make(Options),
|
||||
}
|
||||
for _, mod := range modifiers {
|
||||
mod(&d)
|
||||
}
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// NewDiscoveryForInterface builds a new DHCPv4 Discovery message, with a default
|
||||
// Ethernet HW type and the hardware address obtained from the specified
|
||||
// interface.
|
||||
func NewDiscoveryForInterface(ifname string, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewDiscovery(iface.HardwareAddr, modifiers...)
|
||||
}
|
||||
|
||||
// NewDiscovery builds a new DHCPv4 Discovery message, with a default Ethernet
|
||||
// HW type and specified hardware address.
|
||||
func NewDiscovery(hwaddr net.HardwareAddr, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithHwAddr(hwaddr),
|
||||
WithRequestedOptions(
|
||||
OptionSubnetMask,
|
||||
OptionRouter,
|
||||
OptionDomainName,
|
||||
OptionDomainNameServer,
|
||||
),
|
||||
WithMessageType(MessageTypeDiscover),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewInformForInterface builds a new DHCPv4 Informational message with default
|
||||
// Ethernet HW type and the hardware address obtained from the specified
|
||||
// interface.
|
||||
func NewInformForInterface(ifname string, needsBroadcast bool) (*DHCPv4, error) {
|
||||
// get hw addr
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set Client IP as iface's currently-configured IP.
|
||||
localIPs, err := IPv4AddrsForInterface(iface)
|
||||
if err != nil || len(localIPs) == 0 {
|
||||
return nil, fmt.Errorf("could not get local IPs for iface %s", ifname)
|
||||
}
|
||||
pkt, err := NewInform(iface.HardwareAddr, localIPs[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if needsBroadcast {
|
||||
pkt.SetBroadcast()
|
||||
} else {
|
||||
pkt.SetUnicast()
|
||||
}
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
// PrependModifiers prepends other to m.
|
||||
func PrependModifiers(m []Modifier, other ...Modifier) []Modifier {
|
||||
return append(other, m...)
|
||||
}
|
||||
|
||||
// NewInform builds a new DHCPv4 Informational message with the specified
|
||||
// hardware address.
|
||||
func NewInform(hwaddr net.HardwareAddr, localIP net.IP, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithHwAddr(hwaddr),
|
||||
WithMessageType(MessageTypeInform),
|
||||
WithClientIP(localIP),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewRequestFromOffer builds a DHCPv4 request from an offer.
|
||||
// It assumes the SELECTING state by default, see Section 4.3.2 in RFC 2131 for more details.
|
||||
func NewRequestFromOffer(offer *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithReply(offer),
|
||||
WithMessageType(MessageTypeRequest),
|
||||
WithClientIP(offer.ClientIPAddr),
|
||||
WithOption(OptRequestedIPAddress(offer.YourIPAddr)),
|
||||
// This is usually the server IP.
|
||||
WithOptionCopied(offer, OptionServerIdentifier),
|
||||
WithRequestedOptions(
|
||||
OptionSubnetMask,
|
||||
OptionRouter,
|
||||
OptionDomainName,
|
||||
OptionDomainNameServer,
|
||||
),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewRenewFromAck builds a DHCPv4 RENEW-style request from the ACK of a lease. RENEW requests have
|
||||
// minor changes to their options compared to SELECT requests as specified by RFC 2131, section 4.3.2.
|
||||
func NewRenewFromAck(ack *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithReply(ack),
|
||||
WithMessageType(MessageTypeRequest),
|
||||
// The client IP must be filled in with the IP offered to the client
|
||||
WithClientIP(ack.YourIPAddr),
|
||||
// The renewal request must use unicast
|
||||
WithBroadcast(false),
|
||||
WithRequestedOptions(
|
||||
OptionSubnetMask,
|
||||
OptionRouter,
|
||||
OptionDomainName,
|
||||
OptionDomainNameServer,
|
||||
),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewReplyFromRequest builds a DHCPv4 reply from a request.
|
||||
func NewReplyFromRequest(request *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithReply(request),
|
||||
WithGatewayIP(request.GatewayIPAddr),
|
||||
WithOptionCopied(request, OptionRelayAgentInformation),
|
||||
|
||||
// RFC 6842 states the Client Identifier option must be copied
|
||||
// from the request if a client specified it.
|
||||
WithOptionCopied(request, OptionClientIdentifier),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewReleaseFromACK creates a DHCPv4 Release message from ACK.
|
||||
// default Release message without any Modifer is created as following:
|
||||
// - option Message Type is Release
|
||||
// - ClientIP is set to ack.YourIPAddr
|
||||
// - ClientHWAddr is set to ack.ClientHWAddr
|
||||
// - Unicast
|
||||
// - option Server Identifier is set to ack's ServerIdentifier
|
||||
func NewReleaseFromACK(ack *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithMessageType(MessageTypeRelease),
|
||||
WithClientIP(ack.YourIPAddr),
|
||||
WithHwAddr(ack.ClientHWAddr),
|
||||
WithBroadcast(false),
|
||||
WithOptionCopied(ack, OptionServerIdentifier),
|
||||
)...)
|
||||
}
|
||||
|
||||
// FromBytes decodes a DHCPv4 packet from a sequence of bytes, and returns an
|
||||
// error if the packet is not valid.
|
||||
func FromBytes(q []byte) (*DHCPv4, error) {
|
||||
var p DHCPv4
|
||||
buf := uio.NewBigEndianBuffer(q)
|
||||
|
||||
p.OpCode = OpcodeType(buf.Read8())
|
||||
p.HWType = iana.HWType(buf.Read8())
|
||||
|
||||
hwAddrLen := buf.Read8()
|
||||
|
||||
p.HopCount = buf.Read8()
|
||||
buf.ReadBytes(p.TransactionID[:])
|
||||
p.NumSeconds = buf.Read16()
|
||||
p.Flags = buf.Read16()
|
||||
|
||||
p.ClientIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
p.YourIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
p.ServerIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
p.GatewayIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
|
||||
if hwAddrLen > 16 {
|
||||
hwAddrLen = 16
|
||||
}
|
||||
// Always read 16 bytes, but only use hwaddrlen of them.
|
||||
p.ClientHWAddr = make(net.HardwareAddr, 16)
|
||||
buf.ReadBytes(p.ClientHWAddr)
|
||||
p.ClientHWAddr = p.ClientHWAddr[:hwAddrLen]
|
||||
|
||||
var sname [64]byte
|
||||
buf.ReadBytes(sname[:])
|
||||
length := strings.Index(string(sname[:]), "\x00")
|
||||
if length == -1 {
|
||||
length = 64
|
||||
}
|
||||
p.ServerHostName = string(sname[:length])
|
||||
|
||||
var file [128]byte
|
||||
buf.ReadBytes(file[:])
|
||||
length = strings.Index(string(file[:]), "\x00")
|
||||
if length == -1 {
|
||||
length = 128
|
||||
}
|
||||
p.BootFileName = string(file[:length])
|
||||
|
||||
var cookie [4]byte
|
||||
buf.ReadBytes(cookie[:])
|
||||
|
||||
if err := buf.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cookie != magicCookie {
|
||||
return nil, fmt.Errorf("malformed DHCP packet: got magic cookie %v, want %v", cookie[:], magicCookie[:])
|
||||
}
|
||||
|
||||
p.Options = make(Options)
|
||||
if err := p.Options.fromBytesCheckEnd(buf.Data(), true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// FlagsToString returns a human-readable representation of the flags field.
|
||||
func (d *DHCPv4) FlagsToString() string {
|
||||
flags := ""
|
||||
if d.IsBroadcast() {
|
||||
flags += "Broadcast"
|
||||
} else {
|
||||
flags += "Unicast"
|
||||
}
|
||||
if d.Flags&0xfe != 0 {
|
||||
flags += " (reserved bits not zeroed)"
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
// IsBroadcast indicates whether the packet is a broadcast packet.
|
||||
func (d *DHCPv4) IsBroadcast() bool {
|
||||
return d.Flags&0x8000 == 0x8000
|
||||
}
|
||||
|
||||
// SetBroadcast sets the packet to be a broadcast packet.
|
||||
func (d *DHCPv4) SetBroadcast() {
|
||||
d.Flags |= 0x8000
|
||||
}
|
||||
|
||||
// IsUnicast indicates whether the packet is a unicast packet.
|
||||
func (d *DHCPv4) IsUnicast() bool {
|
||||
return d.Flags&0x8000 == 0
|
||||
}
|
||||
|
||||
// SetUnicast sets the packet to be a unicast packet.
|
||||
func (d *DHCPv4) SetUnicast() {
|
||||
d.Flags &= ^uint16(0x8000)
|
||||
}
|
||||
|
||||
// GetOneOption returns the option that matches the given option code.
|
||||
//
|
||||
// According to RFC 3396, options that are specified more than once are
|
||||
// concatenated, and hence this should always just return one option.
|
||||
func (d *DHCPv4) GetOneOption(code OptionCode) []byte {
|
||||
return d.Options.Get(code)
|
||||
}
|
||||
|
||||
// DeleteOption deletes an existing option with the given option code.
|
||||
func (d *DHCPv4) DeleteOption(code OptionCode) {
|
||||
if d.Options != nil {
|
||||
d.Options.Del(code)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateOption replaces an existing option with the same option code with the
|
||||
// given one, adding it if not already present.
|
||||
func (d *DHCPv4) UpdateOption(opt Option) {
|
||||
if d.Options == nil {
|
||||
d.Options = make(Options)
|
||||
}
|
||||
d.Options.Update(opt)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (d *DHCPv4) String() string {
|
||||
return fmt.Sprintf("DHCPv4(xid=%s hwaddr=%s msg_type=%s, your_ip=%s, server_ip=%s)",
|
||||
d.TransactionID, d.ClientHWAddr, d.MessageType(), d.YourIPAddr, d.ServerIPAddr)
|
||||
}
|
||||
|
||||
// SummaryWithVendor prints a summary of the packet, interpreting the
|
||||
// vendor-specific info option using the given parser (can be nil).
|
||||
func (d *DHCPv4) SummaryWithVendor(vendorDecoder OptionDecoder) string {
|
||||
ret := fmt.Sprintf(
|
||||
"DHCPv4 Message\n"+
|
||||
" opcode: %s\n"+
|
||||
" hwtype: %s\n"+
|
||||
" hopcount: %v\n"+
|
||||
" transaction ID: %s\n"+
|
||||
" num seconds: %v\n"+
|
||||
" flags: %v (0x%02x)\n"+
|
||||
" client IP: %s\n"+
|
||||
" your IP: %s\n"+
|
||||
" server IP: %s\n"+
|
||||
" gateway IP: %s\n"+
|
||||
" client MAC: %s\n"+
|
||||
" server hostname: %s\n"+
|
||||
" bootfile name: %s\n",
|
||||
d.OpCode,
|
||||
d.HWType,
|
||||
d.HopCount,
|
||||
d.TransactionID,
|
||||
d.NumSeconds,
|
||||
d.FlagsToString(),
|
||||
d.Flags,
|
||||
d.ClientIPAddr,
|
||||
d.YourIPAddr,
|
||||
d.ServerIPAddr,
|
||||
d.GatewayIPAddr,
|
||||
d.ClientHWAddr,
|
||||
d.ServerHostName,
|
||||
d.BootFileName,
|
||||
)
|
||||
ret += " options:\n"
|
||||
ret += d.Options.Summary(vendorDecoder)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Summary prints detailed information about the packet.
|
||||
func (d *DHCPv4) Summary() string {
|
||||
return d.SummaryWithVendor(nil)
|
||||
}
|
||||
|
||||
// IsOptionRequested returns true if that option is within the requested
|
||||
// options of the DHCPv4 message.
|
||||
func (d *DHCPv4) IsOptionRequested(requested OptionCode) bool {
|
||||
rq := d.ParameterRequestList()
|
||||
if rq == nil {
|
||||
// RFC2131§3.5
|
||||
// Not all clients require initialization of all parameters [...]
|
||||
// Two techniques are used to reduce the number of parameters transmitted from
|
||||
// the server to the client. [...] Second, in its initial DHCPDISCOVER or
|
||||
// DHCPREQUEST message, a client may provide the server with a list of specific
|
||||
// parameters the client is interested in.
|
||||
// We interpret this to say that all available parameters should be sent if
|
||||
// the parameter request list is not sent at all.
|
||||
return true
|
||||
}
|
||||
|
||||
for _, o := range rq {
|
||||
if o.Code() == requested.Code() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// In case somebody forgets to set an IP, just write 0s as default values.
|
||||
func writeIP(b *uio.Lexer, ip net.IP) {
|
||||
var zeros [net.IPv4len]byte
|
||||
if ip == nil {
|
||||
b.WriteBytes(zeros[:])
|
||||
} else {
|
||||
// Converting IP to 4 byte format
|
||||
ip = ip.To4()
|
||||
b.WriteBytes(ip[:net.IPv4len])
|
||||
}
|
||||
}
|
||||
|
||||
// ToBytes writes the packet to binary.
|
||||
func (d *DHCPv4) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(make([]byte, 0, minPacketLen))
|
||||
buf.Write8(uint8(d.OpCode))
|
||||
buf.Write8(uint8(d.HWType))
|
||||
|
||||
// HwAddrLen
|
||||
hlen := uint8(len(d.ClientHWAddr))
|
||||
buf.Write8(hlen)
|
||||
buf.Write8(d.HopCount)
|
||||
buf.WriteBytes(d.TransactionID[:])
|
||||
buf.Write16(d.NumSeconds)
|
||||
buf.Write16(d.Flags)
|
||||
|
||||
writeIP(buf, d.ClientIPAddr)
|
||||
writeIP(buf, d.YourIPAddr)
|
||||
writeIP(buf, d.ServerIPAddr)
|
||||
writeIP(buf, d.GatewayIPAddr)
|
||||
copy(buf.WriteN(16), d.ClientHWAddr)
|
||||
|
||||
var sname [64]byte
|
||||
copy(sname[:63], []byte(d.ServerHostName))
|
||||
buf.WriteBytes(sname[:])
|
||||
|
||||
var file [128]byte
|
||||
copy(file[:127], []byte(d.BootFileName))
|
||||
buf.WriteBytes(file[:])
|
||||
|
||||
// The magic cookie.
|
||||
buf.WriteBytes(magicCookie[:])
|
||||
|
||||
// Write all options.
|
||||
d.Options.Marshal(buf)
|
||||
|
||||
// Finish the options.
|
||||
buf.Write8(OptionEnd.Code())
|
||||
|
||||
// DHCP is based on BOOTP, and BOOTP messages have a minimum length of
|
||||
// 300 bytes per RFC 951. This not stated explicitly, but if you sum up
|
||||
// all the bytes in the message layout, you'll get 300 bytes.
|
||||
//
|
||||
// Some DHCP servers and relay agents care about this BOOTP legacy B.S.
|
||||
// and "conveniently" drop messages that are less than 300 bytes long.
|
||||
if buf.Len() < bootpMinLen {
|
||||
buf.WriteBytes(bytes.Repeat([]byte{OptionPad.Code()}, bootpMinLen-buf.Len()))
|
||||
}
|
||||
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// GetBroadcastAddress returns the DHCPv4 Broadcast Address value in d.
|
||||
//
|
||||
// The broadcast address option is described in RFC 2132, Section 5.3.
|
||||
func (d *DHCPv4) BroadcastAddress() net.IP {
|
||||
return GetIP(OptionBroadcastAddress, d.Options)
|
||||
}
|
||||
|
||||
// RequestedIPAddress returns the DHCPv4 Requested IP Address value in d.
|
||||
//
|
||||
// The requested IP address option is described by RFC 2132, Section 9.1.
|
||||
func (d *DHCPv4) RequestedIPAddress() net.IP {
|
||||
return GetIP(OptionRequestedIPAddress, d.Options)
|
||||
}
|
||||
|
||||
// ServerIdentifier returns the DHCPv4 Server Identifier value in d.
|
||||
//
|
||||
// The server identifier option is described by RFC 2132, Section 9.7.
|
||||
func (d *DHCPv4) ServerIdentifier() net.IP {
|
||||
return GetIP(OptionServerIdentifier, d.Options)
|
||||
}
|
||||
|
||||
// Router parses the DHCPv4 Router option if present.
|
||||
//
|
||||
// The Router option is described by RFC 2132, Section 3.5.
|
||||
func (d *DHCPv4) Router() []net.IP {
|
||||
return GetIPs(OptionRouter, d.Options)
|
||||
}
|
||||
|
||||
// ClasslessStaticRoute parses the DHCPv4 Classless Static Route option if present.
|
||||
//
|
||||
// The Classless Static Route option is described by RFC 3442.
|
||||
func (d *DHCPv4) ClasslessStaticRoute() []*Route {
|
||||
v := d.Options.Get(OptionClasslessStaticRoute)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var routes Routes
|
||||
if err := routes.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// NTPServers parses the DHCPv4 NTP Servers option if present.
|
||||
//
|
||||
// The NTP servers option is described by RFC 2132, Section 8.3.
|
||||
func (d *DHCPv4) NTPServers() []net.IP {
|
||||
return GetIPs(OptionNTPServers, d.Options)
|
||||
}
|
||||
|
||||
// DNS parses the DHCPv4 Domain Name Server option if present.
|
||||
//
|
||||
// The DNS server option is described by RFC 2132, Section 3.8.
|
||||
func (d *DHCPv4) DNS() []net.IP {
|
||||
return GetIPs(OptionDomainNameServer, d.Options)
|
||||
}
|
||||
|
||||
// DomainName parses the DHCPv4 Domain Name option if present.
|
||||
//
|
||||
// The Domain Name option is described by RFC 2132, Section 3.17.
|
||||
func (d *DHCPv4) DomainName() string {
|
||||
return GetString(OptionDomainName, d.Options)
|
||||
}
|
||||
|
||||
// HostName parses the DHCPv4 Host Name option if present.
|
||||
//
|
||||
// The Host Name option is described by RFC 2132, Section 3.14.
|
||||
func (d *DHCPv4) HostName() string {
|
||||
name := GetString(OptionHostName, d.Options)
|
||||
return strings.TrimRight(name, "\x00")
|
||||
}
|
||||
|
||||
// RootPath parses the DHCPv4 Root Path option if present.
|
||||
//
|
||||
// The Root Path option is described by RFC 2132, Section 3.19.
|
||||
func (d *DHCPv4) RootPath() string {
|
||||
return GetString(OptionRootPath, d.Options)
|
||||
}
|
||||
|
||||
// BootFileNameOption parses the DHCPv4 Bootfile Name option if present.
|
||||
//
|
||||
// The Bootfile Name option is described by RFC 2132, Section 9.5.
|
||||
func (d *DHCPv4) BootFileNameOption() string {
|
||||
name := GetString(OptionBootfileName, d.Options)
|
||||
return strings.TrimRight(name, "\x00")
|
||||
}
|
||||
|
||||
// TFTPServerName parses the DHCPv4 TFTP Server Name option if present.
|
||||
//
|
||||
// The TFTP Server Name option is described by RFC 2132, Section 9.4.
|
||||
func (d *DHCPv4) TFTPServerName() string {
|
||||
name := GetString(OptionTFTPServerName, d.Options)
|
||||
return strings.TrimRight(name, "\x00")
|
||||
}
|
||||
|
||||
// ClassIdentifier parses the DHCPv4 Class Identifier option if present.
|
||||
//
|
||||
// The Vendor Class Identifier option is described by RFC 2132, Section 9.13.
|
||||
func (d *DHCPv4) ClassIdentifier() string {
|
||||
return GetString(OptionClassIdentifier, d.Options)
|
||||
}
|
||||
|
||||
// ClientArch returns the Client System Architecture Type option.
|
||||
func (d *DHCPv4) ClientArch() []iana.Arch {
|
||||
v := d.Options.Get(OptionClientSystemArchitectureType)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var archs iana.Archs
|
||||
if err := archs.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return archs
|
||||
}
|
||||
|
||||
// DomainSearch returns the domain search list if present.
|
||||
//
|
||||
// The domain search option is described by RFC 3397, Section 2.
|
||||
func (d *DHCPv4) DomainSearch() *rfc1035label.Labels {
|
||||
v := d.Options.Get(OptionDNSDomainSearchList)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
labels, err := rfc1035label.FromBytes(v)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
// IPAddressLeaseTime returns the IP address lease time or the given
|
||||
// default duration if not present.
|
||||
//
|
||||
// The IP address lease time option is described by RFC 2132, Section 9.2.
|
||||
func (d *DHCPv4) IPAddressLeaseTime(def time.Duration) time.Duration {
|
||||
v := d.Options.Get(OptionIPAddressLeaseTime)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return def
|
||||
}
|
||||
return time.Duration(dur)
|
||||
}
|
||||
|
||||
// IPAddressRenewalTime returns the IP address renewal time or the given
|
||||
// default duration if not present.
|
||||
//
|
||||
// The IP address renewal time option is described by RFC 2132, Section 9.11.
|
||||
func (d *DHCPv4) IPAddressRenewalTime(def time.Duration) time.Duration {
|
||||
v := d.Options.Get(OptionRenewTimeValue)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return def
|
||||
}
|
||||
return time.Duration(dur)
|
||||
}
|
||||
|
||||
// IPAddressRebindingTime returns the IP address rebinding time or the given
|
||||
// default duration if not present.
|
||||
//
|
||||
// The IP address rebinding time option is described by RFC 2132, Section 9.12.
|
||||
func (d *DHCPv4) IPAddressRebindingTime(def time.Duration) time.Duration {
|
||||
v := d.Options.Get(OptionRebindingTimeValue)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return def
|
||||
}
|
||||
return time.Duration(dur)
|
||||
}
|
||||
|
||||
// IPv6OnlyPreferred returns the V6ONLY_WAIT duration, and a boolean
|
||||
// indicating whether this option was present.
|
||||
//
|
||||
// The IPv6-Only Preferred option is described by RFC 8925, Section 3.1.
|
||||
func (d *DHCPv4) IPv6OnlyPreferred() (time.Duration, bool) {
|
||||
v := d.Options.Get(OptionIPv6OnlyPreferred)
|
||||
if v == nil {
|
||||
return 0, false
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return time.Duration(dur), true
|
||||
}
|
||||
|
||||
// MaxMessageSize returns the DHCP Maximum Message Size if present.
|
||||
//
|
||||
// The Maximum DHCP Message Size option is described by RFC 2132, Section 9.10.
|
||||
func (d *DHCPv4) MaxMessageSize() (uint16, error) {
|
||||
return GetUint16(OptionMaximumDHCPMessageSize, d.Options)
|
||||
}
|
||||
|
||||
// AutoConfigure returns the value of the AutoConfigure option, and a
|
||||
// boolean indicating if it was present.
|
||||
//
|
||||
// The AutoConfigure option is described by RFC 2563, Section 2.
|
||||
func (d *DHCPv4) AutoConfigure() (AutoConfiguration, bool) {
|
||||
v, err := GetByte(OptionAutoConfigure, d.Options)
|
||||
return AutoConfiguration(v), err == nil
|
||||
}
|
||||
|
||||
// MessageType returns the DHCPv4 Message Type option.
|
||||
func (d *DHCPv4) MessageType() MessageType {
|
||||
v := d.Options.Get(OptionDHCPMessageType)
|
||||
if v == nil {
|
||||
return MessageTypeNone
|
||||
}
|
||||
var m MessageType
|
||||
if err := m.FromBytes(v); err != nil {
|
||||
return MessageTypeNone
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Message returns the DHCPv4 (Error) Message option.
|
||||
//
|
||||
// The message options is described in RFC 2132, Section 9.9.
|
||||
func (d *DHCPv4) Message() string {
|
||||
return GetString(OptionMessage, d.Options)
|
||||
}
|
||||
|
||||
// ParameterRequestList returns the DHCPv4 Parameter Request List.
|
||||
//
|
||||
// The parameter request list option is described by RFC 2132, Section 9.8.
|
||||
func (d *DHCPv4) ParameterRequestList() OptionCodeList {
|
||||
v := d.Options.Get(OptionParameterRequestList)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var codes OptionCodeList
|
||||
if err := codes.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return codes
|
||||
}
|
||||
|
||||
// RelayAgentInfo returns options embedded by the relay agent.
|
||||
//
|
||||
// The relay agent info option is described by RFC 3046.
|
||||
func (d *DHCPv4) RelayAgentInfo() *RelayOptions {
|
||||
v := d.Options.Get(OptionRelayAgentInformation)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var relayOptions RelayOptions
|
||||
if err := relayOptions.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &relayOptions
|
||||
}
|
||||
|
||||
// SubnetMask returns a subnet mask option contained if present.
|
||||
//
|
||||
// The subnet mask option is described by RFC 2132, Section 3.3.
|
||||
func (d *DHCPv4) SubnetMask() net.IPMask {
|
||||
v := d.Options.Get(OptionSubnetMask)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var im IPMask
|
||||
if err := im.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return net.IPMask(im)
|
||||
}
|
||||
|
||||
// UserClass returns the user class if present.
|
||||
//
|
||||
// The user class information option is defined by RFC 3004.
|
||||
func (d *DHCPv4) UserClass() []string {
|
||||
v := d.Options.Get(OptionUserClassInformation)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var uc Strings
|
||||
if err := uc.FromBytes(v); err != nil {
|
||||
return []string{GetString(OptionUserClassInformation, d.Options)}
|
||||
}
|
||||
return uc
|
||||
}
|
||||
|
||||
// VIVC returns the vendor-identifying vendor class option if present.
|
||||
func (d *DHCPv4) VIVC() VIVCIdentifiers {
|
||||
v := d.Options.Get(OptionVendorIdentifyingVendorClass)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var ids VIVCIdentifiers
|
||||
if err := ids.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ids
|
||||
}
|
176
vendor/github.com/insomniacslk/dhcp/dhcpv4/modifiers.go
generated
vendored
Normal file
176
vendor/github.com/insomniacslk/dhcp/dhcpv4/modifiers.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
)
|
||||
|
||||
// WithTransactionID sets the Transaction ID for the DHCPv4 packet
|
||||
func WithTransactionID(xid TransactionID) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.TransactionID = xid
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientIP sets the Client IP for a DHCPv4 packet.
|
||||
func WithClientIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.ClientIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithYourIP sets the Your IP for a DHCPv4 packet.
|
||||
func WithYourIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.YourIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithServerIP sets the Server IP for a DHCPv4 packet.
|
||||
func WithServerIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.ServerIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithGatewayIP sets the Gateway IP for the DHCPv4 packet.
|
||||
func WithGatewayIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.GatewayIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithOptionCopied copies the value of option opt from request.
|
||||
func WithOptionCopied(request *DHCPv4, opt OptionCode) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
if val := request.Options.Get(opt); val != nil {
|
||||
d.UpdateOption(OptGeneric(opt, val))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithReply fills in opcode, hwtype, xid, clienthwaddr, and flags from the given packet.
|
||||
func WithReply(request *DHCPv4) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
if request.OpCode == OpcodeBootRequest {
|
||||
d.OpCode = OpcodeBootReply
|
||||
} else {
|
||||
d.OpCode = OpcodeBootRequest
|
||||
}
|
||||
d.HWType = request.HWType
|
||||
d.TransactionID = request.TransactionID
|
||||
d.ClientHWAddr = request.ClientHWAddr
|
||||
d.Flags = request.Flags
|
||||
}
|
||||
}
|
||||
|
||||
// WithHWType sets the Hardware Type for a DHCPv4 packet.
|
||||
func WithHWType(hwt iana.HWType) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.HWType = hwt
|
||||
}
|
||||
}
|
||||
|
||||
// WithBroadcast sets the packet to be broadcast or unicast
|
||||
func WithBroadcast(broadcast bool) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
if broadcast {
|
||||
d.SetBroadcast()
|
||||
} else {
|
||||
d.SetUnicast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHwAddr sets the hardware address for a packet
|
||||
func WithHwAddr(hwaddr net.HardwareAddr) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.ClientHWAddr = hwaddr
|
||||
}
|
||||
}
|
||||
|
||||
// WithOption appends a DHCPv4 option provided by the user
|
||||
func WithOption(opt Option) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.UpdateOption(opt)
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutOption removes the DHCPv4 option with the given code
|
||||
func WithoutOption(code OptionCode) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.DeleteOption(code)
|
||||
}
|
||||
}
|
||||
|
||||
// WithUserClass adds a user class option to the packet.
|
||||
// The rfc parameter allows you to specify if the userclass should be
|
||||
// rfc compliant or not. More details in issue #113
|
||||
func WithUserClass(uc string, rfc bool) Modifier {
|
||||
// TODO let the user specify multiple user classes
|
||||
return func(d *DHCPv4) {
|
||||
if rfc {
|
||||
d.UpdateOption(OptRFC3004UserClass([]string{uc}))
|
||||
} else {
|
||||
d.UpdateOption(OptUserClass(uc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithNetboot adds bootfile URL and bootfile param options to a DHCPv4 packet.
|
||||
func WithNetboot(d *DHCPv4) {
|
||||
WithRequestedOptions(OptionTFTPServerName, OptionBootfileName)(d)
|
||||
}
|
||||
|
||||
// WithMessageType adds the DHCPv4 message type m to a packet.
|
||||
func WithMessageType(m MessageType) Modifier {
|
||||
return WithOption(OptMessageType(m))
|
||||
}
|
||||
|
||||
// WithRequestedOptions adds requested options to the packet.
|
||||
func WithRequestedOptions(optionCodes ...OptionCode) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
cl := d.ParameterRequestList()
|
||||
cl.Add(optionCodes...)
|
||||
d.UpdateOption(OptParameterRequestList(cl...))
|
||||
}
|
||||
}
|
||||
|
||||
// WithRelay adds parameters required for DHCPv4 to be relayed by the relay
|
||||
// server with given ip
|
||||
func WithRelay(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.SetUnicast()
|
||||
d.GatewayIPAddr = ip
|
||||
d.HopCount++
|
||||
}
|
||||
}
|
||||
|
||||
// WithNetmask adds or updates an OptSubnetMask
|
||||
func WithNetmask(mask net.IPMask) Modifier {
|
||||
return WithOption(OptSubnetMask(mask))
|
||||
}
|
||||
|
||||
// WithLeaseTime adds or updates an OptIPAddressLeaseTime
|
||||
func WithLeaseTime(leaseTime uint32) Modifier {
|
||||
return WithOption(OptIPAddressLeaseTime(time.Duration(leaseTime) * time.Second))
|
||||
}
|
||||
|
||||
// WithIPv6OnlyPreferred adds or updates an OptIPv6OnlyPreferred
|
||||
func WithIPv6OnlyPreferred(v6OnlyWait uint32) Modifier {
|
||||
return WithOption(OptIPv6OnlyPreferred(time.Duration(v6OnlyWait) * time.Second))
|
||||
}
|
||||
|
||||
// WithDomainSearchList adds or updates an OptionDomainSearch
|
||||
func WithDomainSearchList(searchList ...string) Modifier {
|
||||
return WithOption(OptDomainSearch(&rfc1035label.Labels{
|
||||
Labels: searchList,
|
||||
}))
|
||||
}
|
||||
|
||||
func WithGeneric(code OptionCode, value []byte) Modifier {
|
||||
return WithOption(OptGeneric(code, value))
|
||||
}
|
669
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/client.go
generated
vendored
Normal file
669
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/client.go
generated
vendored
Normal file
@ -0,0 +1,669 @@
|
||||
// Copyright 2018 the u-root Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.12
|
||||
|
||||
// Package nclient4 is a small, minimum-functionality client for DHCPv4.
|
||||
//
|
||||
// It only supports the 4-way DHCPv4 Discover-Offer-Request-Ack handshake as
|
||||
// well as the Request-Ack renewal process.
|
||||
package nclient4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBufferCap = 5
|
||||
|
||||
// DefaultTimeout is the default value for read-timeout if option WithTimeout is not set
|
||||
DefaultTimeout = 5 * time.Second
|
||||
|
||||
// DefaultRetries is amount of retries will be done if no answer was received within read-timeout amount of time
|
||||
DefaultRetries = 3
|
||||
|
||||
// MaxMessageSize is the value to be used for DHCP option "MaxMessageSize".
|
||||
MaxMessageSize = 1500
|
||||
|
||||
// ClientPort is the port that DHCP clients listen on.
|
||||
ClientPort = 68
|
||||
|
||||
// ServerPort is the port that DHCP servers and relay agents listen on.
|
||||
ServerPort = 67
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultServers is the address of all link-local DHCP servers and
|
||||
// relay agents.
|
||||
DefaultServers = &net.UDPAddr{
|
||||
IP: net.IPv4bcast,
|
||||
Port: ServerPort,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoResponse is returned when no response packet is received.
|
||||
ErrNoResponse = errors.New("no matching response packet received")
|
||||
|
||||
// ErrNoConn is returned when NewWithConn is called with nil-value as conn.
|
||||
ErrNoConn = errors.New("conn is nil")
|
||||
|
||||
// ErrNoIfaceHWAddr is returned when NewWithConn is called with nil-value as ifaceHWAddr
|
||||
ErrNoIfaceHWAddr = errors.New("ifaceHWAddr is nil")
|
||||
)
|
||||
|
||||
// pendingCh is a channel associated with a pending TransactionID.
|
||||
type pendingCh struct {
|
||||
// SendAndRead closes done to indicate that it wishes for no more
|
||||
// messages for this particular XID.
|
||||
done <-chan struct{}
|
||||
|
||||
// ch is used by the receive loop to distribute DHCP messages.
|
||||
ch chan<- *dhcpv4.DHCPv4
|
||||
}
|
||||
|
||||
// Logger is a handler which will be used to output logging messages
|
||||
type Logger interface {
|
||||
// PrintMessage print _all_ DHCP messages
|
||||
PrintMessage(prefix string, message *dhcpv4.DHCPv4)
|
||||
|
||||
// Printf is use to print the rest debugging information
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// EmptyLogger prints nothing
|
||||
type EmptyLogger struct{}
|
||||
|
||||
// Printf is just a dummy function that does nothing
|
||||
func (e EmptyLogger) Printf(format string, v ...interface{}) {}
|
||||
|
||||
// PrintMessage is just a dummy function that does nothing
|
||||
func (e EmptyLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {}
|
||||
|
||||
// Printfer is used for actual output of the logger. For example *log.Logger is a Printfer.
|
||||
type Printfer interface {
|
||||
// Printf is the function for logging output. Arguments are handled in the manner of fmt.Printf.
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// ShortSummaryLogger is a wrapper for Printfer to implement interface Logger.
|
||||
// DHCP messages are printed in the short format.
|
||||
type ShortSummaryLogger struct {
|
||||
// Printfer is used for actual output of the logger
|
||||
Printfer
|
||||
}
|
||||
|
||||
// Printf prints a log message as-is via predefined Printfer
|
||||
func (s ShortSummaryLogger) Printf(format string, v ...interface{}) {
|
||||
s.Printfer.Printf(format, v...)
|
||||
}
|
||||
|
||||
// PrintMessage prints a DHCP message in the short format via predefined Printfer
|
||||
func (s ShortSummaryLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {
|
||||
s.Printf("%s: %s", prefix, message)
|
||||
}
|
||||
|
||||
// DebugLogger is a wrapper for Printfer to implement interface Logger.
|
||||
// DHCP messages are printed in the long format.
|
||||
type DebugLogger struct {
|
||||
// Printfer is used for actual output of the logger
|
||||
Printfer
|
||||
}
|
||||
|
||||
// Printf prints a log message as-is via predefined Printfer
|
||||
func (d DebugLogger) Printf(format string, v ...interface{}) {
|
||||
d.Printfer.Printf(format, v...)
|
||||
}
|
||||
|
||||
// PrintMessage prints a DHCP message in the long format via predefined Printfer
|
||||
func (d DebugLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {
|
||||
d.Printf("%s: %s", prefix, message.Summary())
|
||||
}
|
||||
|
||||
// Client is an IPv4 DHCP client.
|
||||
type Client struct {
|
||||
ifaceHWAddr net.HardwareAddr
|
||||
conn net.PacketConn
|
||||
timeout time.Duration
|
||||
retry int
|
||||
logger Logger
|
||||
|
||||
// bufferCap is the channel capacity for each TransactionID.
|
||||
bufferCap int
|
||||
|
||||
// serverAddr is the UDP address to send all packets to.
|
||||
//
|
||||
// This may be an actual broadcast address, or a unicast address.
|
||||
serverAddr *net.UDPAddr
|
||||
|
||||
// closed is an atomic bool set to 1 when done is closed.
|
||||
closed uint32
|
||||
|
||||
// done is closed to unblock the receive loop.
|
||||
done chan struct{}
|
||||
|
||||
// wg protects any spawned goroutines, namely the receiveLoop.
|
||||
wg sync.WaitGroup
|
||||
|
||||
pendingMu sync.Mutex
|
||||
// pending stores the distribution channels for each pending
|
||||
// TransactionID. receiveLoop uses this map to determine which channel
|
||||
// to send a new DHCP message to.
|
||||
pending map[dhcpv4.TransactionID]*pendingCh
|
||||
}
|
||||
|
||||
// New returns a client usable with an unconfigured interface.
|
||||
func New(iface string, opts ...ClientOpt) (*Client, error) {
|
||||
return new(iface, nil, nil, opts...)
|
||||
}
|
||||
|
||||
// NewWithConn creates a new DHCP client that sends and receives packets on the
|
||||
// given interface.
|
||||
func NewWithConn(conn net.PacketConn, ifaceHWAddr net.HardwareAddr, opts ...ClientOpt) (*Client, error) {
|
||||
return new(``, conn, ifaceHWAddr, opts...)
|
||||
}
|
||||
|
||||
func new(iface string, conn net.PacketConn, ifaceHWAddr net.HardwareAddr, opts ...ClientOpt) (*Client, error) {
|
||||
c := &Client{
|
||||
ifaceHWAddr: ifaceHWAddr,
|
||||
timeout: DefaultTimeout,
|
||||
retry: DefaultRetries,
|
||||
serverAddr: DefaultServers,
|
||||
bufferCap: defaultBufferCap,
|
||||
conn: conn,
|
||||
logger: EmptyLogger{},
|
||||
|
||||
done: make(chan struct{}),
|
||||
pending: make(map[dhcpv4.TransactionID]*pendingCh),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to apply option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.ifaceHWAddr == nil {
|
||||
if iface == `` {
|
||||
return nil, ErrNoIfaceHWAddr
|
||||
}
|
||||
|
||||
i, err := net.InterfaceByName(iface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get interface information: %w", err)
|
||||
}
|
||||
|
||||
c.ifaceHWAddr = i.HardwareAddr
|
||||
}
|
||||
|
||||
if c.conn == nil {
|
||||
var err error
|
||||
if iface == `` {
|
||||
return nil, ErrNoConn
|
||||
}
|
||||
c.conn, err = NewRawUDPConn(iface, ClientPort) // broadcast
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open a broadcasting socket: %w", err)
|
||||
}
|
||||
}
|
||||
c.wg.Add(1)
|
||||
go c.receiveLoop()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Close closes the underlying connection.
|
||||
func (c *Client) Close() error {
|
||||
// Make sure not to close done twice.
|
||||
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := c.conn.Close()
|
||||
|
||||
// Closing c.done sets off a chain reaction:
|
||||
//
|
||||
// Any SendAndRead unblocks trying to receive more messages, which
|
||||
// means rem() gets called.
|
||||
//
|
||||
// rem() should be unblocking receiveLoop if it is blocked.
|
||||
//
|
||||
// receiveLoop should then exit gracefully.
|
||||
close(c.done)
|
||||
|
||||
// Wait for receiveLoop to stop.
|
||||
c.wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) isClosed() bool {
|
||||
return atomic.LoadUint32(&c.closed) != 0
|
||||
}
|
||||
|
||||
func (c *Client) receiveLoop() {
|
||||
defer c.wg.Done()
|
||||
for {
|
||||
// TODO: Clients can send a "max packet size" option in their
|
||||
// packets, IIRC. Choose a reasonable size and set it.
|
||||
b := make([]byte, MaxMessageSize)
|
||||
n, _, err := c.conn.ReadFrom(b)
|
||||
if err != nil {
|
||||
if !c.isClosed() {
|
||||
c.logger.Printf("error reading from UDP connection: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := dhcpv4.FromBytes(b[:n])
|
||||
if err != nil {
|
||||
// Not a valid DHCP packet; keep listening.
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.OpCode != dhcpv4.OpcodeBootReply {
|
||||
// Not a response message.
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a somewhat non-standard check, by the looks
|
||||
// of RFC 2131. It should work as long as the DHCP
|
||||
// server is spec-compliant for the HWAddr field.
|
||||
if c.ifaceHWAddr != nil && !bytes.Equal(c.ifaceHWAddr, msg.ClientHWAddr) {
|
||||
// Not for us.
|
||||
continue
|
||||
}
|
||||
|
||||
c.pendingMu.Lock()
|
||||
p, ok := c.pending[msg.TransactionID]
|
||||
if ok {
|
||||
select {
|
||||
case <-p.done:
|
||||
close(p.ch)
|
||||
delete(c.pending, msg.TransactionID)
|
||||
|
||||
// This send may block.
|
||||
case p.ch <- msg:
|
||||
}
|
||||
}
|
||||
c.pendingMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOpt is a function that configures the Client.
|
||||
type ClientOpt func(c *Client) error
|
||||
|
||||
// WithTimeout configures the retransmission timeout.
|
||||
//
|
||||
// Default is 5 seconds.
|
||||
func WithTimeout(d time.Duration) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.timeout = d
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithSummaryLogger logs one-line DHCPv4 message summaries when sent & received.
|
||||
func WithSummaryLogger() ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.logger = ShortSummaryLogger{
|
||||
Printfer: log.New(os.Stderr, "[dhcpv4] ", log.LstdFlags),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithDebugLogger logs multi-line full DHCPv4 messages when sent & received.
|
||||
func WithDebugLogger() ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.logger = DebugLogger{
|
||||
Printfer: log.New(os.Stderr, "[dhcpv4] ", log.LstdFlags),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger set the logger (see interface Logger).
|
||||
func WithLogger(newLogger Logger) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.logger = newLogger
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithUnicast forces client to send messages as unicast frames.
|
||||
// By default client sends messages as broadcast frames even if server address is defined.
|
||||
//
|
||||
// srcAddr is both:
|
||||
// * The source address of outgoing frames.
|
||||
// * The address to be listened for incoming frames.
|
||||
func WithUnicast(srcAddr *net.UDPAddr) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
if srcAddr == nil {
|
||||
srcAddr = &net.UDPAddr{Port: ClientPort}
|
||||
}
|
||||
c.conn, err = net.ListenUDP("udp4", srcAddr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to start listening UDP port: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithHWAddr tells to the Client to receive messages destinated to selected
|
||||
// hardware address
|
||||
func WithHWAddr(hwAddr net.HardwareAddr) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.ifaceHWAddr = hwAddr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func withBufferCap(n int) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.bufferCap = n
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetry configures the number of retransmissions to attempt.
|
||||
//
|
||||
// Default is 3.
|
||||
func WithRetry(r int) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.retry = r
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithServerAddr configures the address to send messages to.
|
||||
func WithServerAddr(n *net.UDPAddr) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.serverAddr = n
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Matcher matches DHCP packets.
|
||||
type Matcher func(*dhcpv4.DHCPv4) bool
|
||||
|
||||
// IsMessageType returns a matcher that checks for the message types.
|
||||
func IsMessageType(t dhcpv4.MessageType, tt ...dhcpv4.MessageType) Matcher {
|
||||
return func(p *dhcpv4.DHCPv4) bool {
|
||||
if p.MessageType() == t {
|
||||
return true
|
||||
}
|
||||
for _, mt := range tt {
|
||||
if p.MessageType() == mt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsCorrectServer returns a matcher that checks for the correct ServerAddress.
|
||||
func IsCorrectServer(s net.IP) Matcher {
|
||||
return func(p *dhcpv4.DHCPv4) bool {
|
||||
return p.ServerIdentifier().Equal(s)
|
||||
}
|
||||
}
|
||||
|
||||
// IsAll returns a matcher that checks for all given matchers to be true.
|
||||
func IsAll(ms ...Matcher) Matcher {
|
||||
return func(p *dhcpv4.DHCPv4) bool {
|
||||
for _, m := range ms {
|
||||
if !m(p) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// RemoteAddr is the default DHCP server address this client sends messages to.
|
||||
func (c *Client) RemoteAddr() *net.UDPAddr {
|
||||
// Make a copy so the caller cannot modify the address once the client
|
||||
// is running.
|
||||
cop := *c.serverAddr
|
||||
return &cop
|
||||
}
|
||||
|
||||
// InterfaceAddr returns the MAC address of the client's interface.
|
||||
func (c *Client) InterfaceAddr() net.HardwareAddr {
|
||||
b := make(net.HardwareAddr, len(c.ifaceHWAddr))
|
||||
copy(b, c.ifaceHWAddr)
|
||||
return b
|
||||
}
|
||||
|
||||
// DiscoverOffer sends a DHCPDiscover message and returns the first valid offer
|
||||
// received.
|
||||
func (c *Client) DiscoverOffer(ctx context.Context, modifiers ...dhcpv4.Modifier) (offer *dhcpv4.DHCPv4, err error) {
|
||||
// RFC 2131, Section 4.4.1, Table 5 details what a DISCOVER packet should
|
||||
// contain.
|
||||
discover, err := dhcpv4.NewDiscovery(c.ifaceHWAddr, dhcpv4.PrependModifiers(modifiers,
|
||||
dhcpv4.WithOption(dhcpv4.OptMaxMessageSize(MaxMessageSize)))...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a discovery request: %w", err)
|
||||
}
|
||||
|
||||
offer, err = c.SendAndRead(ctx, c.serverAddr, discover, IsMessageType(dhcpv4.MessageTypeOffer))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got an error while the discovery request: %w", err)
|
||||
}
|
||||
return offer, nil
|
||||
}
|
||||
|
||||
// Request completes the 4-way Discover-Offer-Request-Ack handshake.
|
||||
//
|
||||
// Note that modifiers will be applied *both* to Discover and Request packets.
|
||||
func (c *Client) Request(ctx context.Context, modifiers ...dhcpv4.Modifier) (lease *Lease, err error) {
|
||||
offer, err := c.DiscoverOffer(ctx, modifiers...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to receive an offer: %w", err)
|
||||
return
|
||||
}
|
||||
return c.RequestFromOffer(ctx, offer, modifiers...)
|
||||
}
|
||||
|
||||
// Inform sends an INFORM request using the given local IP.
|
||||
// Returns the ACK response from the server on success.
|
||||
func (c *Client) Inform(ctx context.Context, localIP net.IP, modifiers ...dhcpv4.Modifier) (*dhcpv4.DHCPv4, error) {
|
||||
request, err := dhcpv4.NewInform(c.ifaceHWAddr, localIP, modifiers...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// DHCP clients must not fill in the server identifier in an INFORM request as per RFC 2131 Section 4.4.1 Table 5,
|
||||
// however, they may still unicast the request to the target server if the address is known (c.serverAddr), as per
|
||||
// Section 4.4.3. The server must then respond with an ACK, as per Section 4.3.5.
|
||||
response, err := c.SendAndRead(ctx, c.serverAddr, request, IsMessageType(dhcpv4.MessageTypeAck))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got an error while processing the request: %w", err)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ErrNak is returned if a DHCP server rejected our Request.
|
||||
type ErrNak struct {
|
||||
Offer *dhcpv4.DHCPv4
|
||||
Nak *dhcpv4.DHCPv4
|
||||
}
|
||||
|
||||
// Error implements error.Error.
|
||||
func (e *ErrNak) Error() string {
|
||||
if msg := e.Nak.Message(); len(msg) > 0 {
|
||||
return fmt.Sprintf("server rejected request with Nak (msg: %s)", msg)
|
||||
}
|
||||
return "server rejected request with Nak"
|
||||
}
|
||||
|
||||
// RequestFromOffer sends a Request message and waits for an response.
|
||||
// It assumes the SELECTING state by default, see Section 4.3.2 in RFC 2131 for more details.
|
||||
func (c *Client) RequestFromOffer(ctx context.Context, offer *dhcpv4.DHCPv4, modifiers ...dhcpv4.Modifier) (*Lease, error) {
|
||||
// TODO(chrisko): should this be unicast to the server?
|
||||
request, err := dhcpv4.NewRequestFromOffer(offer, dhcpv4.PrependModifiers(modifiers,
|
||||
dhcpv4.WithOption(dhcpv4.OptMaxMessageSize(MaxMessageSize)))...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a request: %w", err)
|
||||
}
|
||||
|
||||
// Servers are supposed to only respond to Requests containing their server identifier,
|
||||
// but sometimes non-compliant servers respond anyway.
|
||||
// Clients are not required to validate this field, but servers are required to
|
||||
// include the server identifier in their Offer per RFC 2131 Section 4.3.1 Table 3.
|
||||
response, err := c.SendAndRead(ctx, c.serverAddr, request, IsAll(
|
||||
IsCorrectServer(offer.ServerIdentifier()),
|
||||
IsMessageType(dhcpv4.MessageTypeAck, dhcpv4.MessageTypeNak)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got an error while processing the request: %w", err)
|
||||
}
|
||||
if response.MessageType() == dhcpv4.MessageTypeNak {
|
||||
return nil, &ErrNak{
|
||||
Offer: offer,
|
||||
Nak: response,
|
||||
}
|
||||
}
|
||||
lease := &Lease{}
|
||||
lease.ACK = response
|
||||
lease.Offer = offer
|
||||
lease.CreationTime = time.Now()
|
||||
return lease, nil
|
||||
}
|
||||
|
||||
// ErrTransactionIDInUse is returned if there were an attempt to send a message
|
||||
// with the same TransactionID as we are already waiting an answer for.
|
||||
type ErrTransactionIDInUse struct {
|
||||
// TransactionID is the transaction ID of the message which the error is related to.
|
||||
TransactionID dhcpv4.TransactionID
|
||||
}
|
||||
|
||||
// Error is just the method to comply interface "error".
|
||||
func (err *ErrTransactionIDInUse) Error() string {
|
||||
return fmt.Sprintf("transaction ID %s already in use", err.TransactionID)
|
||||
}
|
||||
|
||||
// send sends p to destination and returns a response channel.
|
||||
//
|
||||
// Responses will be matched by transaction ID and ClientHWAddr.
|
||||
//
|
||||
// The returned lambda function must be called after all desired responses have
|
||||
// been received in order to return the Transaction ID to the usable pool.
|
||||
func (c *Client) send(dest *net.UDPAddr, msg *dhcpv4.DHCPv4) (resp <-chan *dhcpv4.DHCPv4, cancel func(), err error) {
|
||||
c.pendingMu.Lock()
|
||||
if _, ok := c.pending[msg.TransactionID]; ok {
|
||||
c.pendingMu.Unlock()
|
||||
return nil, nil, &ErrTransactionIDInUse{msg.TransactionID}
|
||||
}
|
||||
|
||||
ch := make(chan *dhcpv4.DHCPv4, c.bufferCap)
|
||||
done := make(chan struct{})
|
||||
c.pending[msg.TransactionID] = &pendingCh{done: done, ch: ch}
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
cancel = func() {
|
||||
// Why can't we just close ch here?
|
||||
//
|
||||
// Because receiveLoop may potentially be blocked trying to
|
||||
// send on ch. We gotta unblock it first, and then we can take
|
||||
// the lock and remove the XID from the pending transaction
|
||||
// map.
|
||||
close(done)
|
||||
|
||||
c.pendingMu.Lock()
|
||||
if p, ok := c.pending[msg.TransactionID]; ok {
|
||||
close(p.ch)
|
||||
delete(c.pending, msg.TransactionID)
|
||||
}
|
||||
c.pendingMu.Unlock()
|
||||
}
|
||||
|
||||
if _, err := c.conn.WriteTo(msg.ToBytes(), dest); err != nil {
|
||||
cancel()
|
||||
return nil, nil, fmt.Errorf("error writing packet to connection: %w", err)
|
||||
}
|
||||
return ch, cancel, nil
|
||||
}
|
||||
|
||||
// This error should never be visible to users.
|
||||
// It is used only to increase the timeout in retryFn.
|
||||
var errDeadlineExceeded = errors.New("INTERNAL ERROR: deadline exceeded")
|
||||
|
||||
// SendAndRead sends a packet p to a destination dest and waits for the first
|
||||
// response matching `match` as well as its Transaction ID and ClientHWAddr.
|
||||
//
|
||||
// If match is nil, the first packet matching the Transaction ID and
|
||||
// ClientHWAddr is returned.
|
||||
func (c *Client) SendAndRead(ctx context.Context, dest *net.UDPAddr, p *dhcpv4.DHCPv4, match Matcher) (*dhcpv4.DHCPv4, error) {
|
||||
var response *dhcpv4.DHCPv4
|
||||
err := c.retryFn(func(timeout time.Duration) error {
|
||||
ch, rem, err := c.send(dest, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.logger.PrintMessage("sent message", p)
|
||||
defer rem()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.done:
|
||||
return ErrNoResponse
|
||||
|
||||
case <-time.After(timeout):
|
||||
return errDeadlineExceeded
|
||||
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
|
||||
case packet := <-ch:
|
||||
if match == nil || match(packet) {
|
||||
c.logger.PrintMessage("received message", packet)
|
||||
response = packet
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if err == errDeadlineExceeded {
|
||||
return nil, ErrNoResponse
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) retryFn(fn func(timeout time.Duration) error) error {
|
||||
timeout := c.timeout
|
||||
|
||||
// Each retry takes the amount of timeout at worst.
|
||||
for i := 0; i < c.retry || c.retry < 0; i++ { // TODO: why is this called "retry" if this is "tries" ("retries"+1)?
|
||||
switch err := fn(timeout); err {
|
||||
case nil:
|
||||
// Got it!
|
||||
return nil
|
||||
|
||||
case errDeadlineExceeded:
|
||||
// Double timeout, then retry.
|
||||
timeout *= 2
|
||||
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return errDeadlineExceeded
|
||||
}
|
154
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/conn_unix.go
generated
vendored
Normal file
154
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/conn_unix.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2018 the u-root Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.12 && (darwin || freebsd || linux || netbsd || openbsd || dragonfly)
|
||||
// +build go1.12
|
||||
// +build darwin freebsd linux netbsd openbsd dragonfly
|
||||
|
||||
package nclient4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/mdlayher/packet"
|
||||
"github.com/u-root/uio/uio"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
// BroadcastMac is the broadcast MAC address.
|
||||
//
|
||||
// Any UDP packet sent to this address is broadcast on the subnet.
|
||||
BroadcastMac = net.HardwareAddr([]byte{255, 255, 255, 255, 255, 255})
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUDPAddrIsRequired is an error used when a passed argument is not of type "*net.UDPAddr".
|
||||
ErrUDPAddrIsRequired = errors.New("must supply UDPAddr")
|
||||
)
|
||||
|
||||
// NewRawUDPConn returns a UDP connection bound to the interface and port
|
||||
// given based on a raw packet socket. All packets are broadcasted.
|
||||
//
|
||||
// The interface can be completely unconfigured.
|
||||
func NewRawUDPConn(iface string, port int) (net.PacketConn, error) {
|
||||
ifc, err := net.InterfaceByName(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawConn, err := packet.Listen(ifc, packet.Datagram, unix.ETH_P_IP, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewBroadcastUDPConn(rawConn, &net.UDPAddr{Port: port}), nil
|
||||
}
|
||||
|
||||
// BroadcastRawUDPConn uses a raw socket to send UDP packets to the broadcast
|
||||
// MAC address.
|
||||
type BroadcastRawUDPConn struct {
|
||||
// PacketConn is a raw DGRAM socket.
|
||||
net.PacketConn
|
||||
|
||||
// boundAddr is the address this RawUDPConn is "bound" to.
|
||||
//
|
||||
// Calls to ReadFrom will only return packets destined to this address.
|
||||
boundAddr *net.UDPAddr
|
||||
}
|
||||
|
||||
// NewBroadcastUDPConn returns a PacketConn that marshals and unmarshals UDP
|
||||
// packets, sending them to the broadcast MAC at on rawPacketConn.
|
||||
//
|
||||
// Calls to ReadFrom will only return packets destined to boundAddr.
|
||||
func NewBroadcastUDPConn(rawPacketConn net.PacketConn, boundAddr *net.UDPAddr) net.PacketConn {
|
||||
return &BroadcastRawUDPConn{
|
||||
PacketConn: rawPacketConn,
|
||||
boundAddr: boundAddr,
|
||||
}
|
||||
}
|
||||
|
||||
func udpMatch(addr *net.UDPAddr, bound *net.UDPAddr) bool {
|
||||
if bound == nil {
|
||||
return true
|
||||
}
|
||||
if bound.IP != nil && !bound.IP.Equal(addr.IP) {
|
||||
return false
|
||||
}
|
||||
return bound.Port == addr.Port
|
||||
}
|
||||
|
||||
// ReadFrom implements net.PacketConn.ReadFrom.
|
||||
//
|
||||
// ReadFrom reads raw IP packets and will try to match them against
|
||||
// upc.boundAddr. Any matching packets are returned via the given buffer.
|
||||
func (upc *BroadcastRawUDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
ipHdrMaxLen := ipv4MaximumHeaderSize
|
||||
udpHdrLen := udpMinimumSize
|
||||
|
||||
for {
|
||||
pkt := make([]byte, ipHdrMaxLen+udpHdrLen+len(b))
|
||||
n, _, err := upc.PacketConn.ReadFrom(pkt)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
pkt = pkt[:n]
|
||||
buf := uio.NewBigEndianBuffer(pkt)
|
||||
|
||||
ipHdr := ipv4(buf.Data())
|
||||
|
||||
if !ipHdr.isValid(n) {
|
||||
continue
|
||||
}
|
||||
|
||||
ipHdr = ipv4(buf.Consume(int(ipHdr.headerLength())))
|
||||
|
||||
if ipHdr.transportProtocol() != udpProtocolNumber {
|
||||
continue
|
||||
}
|
||||
|
||||
if !buf.Has(udpHdrLen) {
|
||||
continue
|
||||
}
|
||||
|
||||
udpHdr := udp(buf.Consume(udpHdrLen))
|
||||
|
||||
addr := &net.UDPAddr{
|
||||
IP: ipHdr.destinationAddress(),
|
||||
Port: int(udpHdr.destinationPort()),
|
||||
}
|
||||
if !udpMatch(addr, upc.boundAddr) {
|
||||
continue
|
||||
}
|
||||
srcAddr := &net.UDPAddr{
|
||||
IP: ipHdr.sourceAddress(),
|
||||
Port: int(udpHdr.sourcePort()),
|
||||
}
|
||||
// Extra padding after end of IP packet should be ignored,
|
||||
// if not dhcp option parsing will fail.
|
||||
dhcpLen := int(ipHdr.payloadLength()) - udpHdrLen
|
||||
return copy(b, buf.Consume(dhcpLen)), srcAddr, nil
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo implements net.PacketConn.WriteTo and broadcasts all packets at the
|
||||
// raw socket level.
|
||||
//
|
||||
// WriteTo wraps the given packet in the appropriate UDP and IP header before
|
||||
// sending it on the packet conn.
|
||||
func (upc *BroadcastRawUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return 0, ErrUDPAddrIsRequired
|
||||
}
|
||||
|
||||
// Using the boundAddr is not quite right here, but it works.
|
||||
pkt := udp4pkt(b, udpAddr, upc.boundAddr)
|
||||
|
||||
// Broadcasting is not always right, but hell, what the ARP do I know.
|
||||
return upc.PacketConn.WriteTo(pkt, &packet.Addr{HardwareAddr: BroadcastMac})
|
||||
}
|
360
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/ipv4.go
generated
vendored
Normal file
360
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/ipv4.go
generated
vendored
Normal file
@ -0,0 +1,360 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// This file contains code taken from gVisor.
|
||||
|
||||
//go:build go1.12
|
||||
// +build go1.12
|
||||
|
||||
package nclient4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
const (
|
||||
versIHL = 0
|
||||
tos = 1
|
||||
totalLen = 2
|
||||
id = 4
|
||||
flagsFO = 6
|
||||
ttl = 8
|
||||
protocol = 9
|
||||
checksumOff = 10
|
||||
srcAddr = 12
|
||||
dstAddr = 16
|
||||
)
|
||||
|
||||
// transportProtocolNumber is the number of a transport protocol.
|
||||
type transportProtocolNumber uint32
|
||||
|
||||
// ipv4Fields contains the fields of an IPv4 packet. It is used to describe the
|
||||
// fields of a packet that needs to be encoded.
|
||||
type ipv4Fields struct {
|
||||
// IHL is the "internet header length" field of an IPv4 packet.
|
||||
IHL uint8
|
||||
|
||||
// TOS is the "type of service" field of an IPv4 packet.
|
||||
TOS uint8
|
||||
|
||||
// TotalLength is the "total length" field of an IPv4 packet.
|
||||
TotalLength uint16
|
||||
|
||||
// ID is the "identification" field of an IPv4 packet.
|
||||
ID uint16
|
||||
|
||||
// Flags is the "flags" field of an IPv4 packet.
|
||||
Flags uint8
|
||||
|
||||
// FragmentOffset is the "fragment offset" field of an IPv4 packet.
|
||||
FragmentOffset uint16
|
||||
|
||||
// TTL is the "time to live" field of an IPv4 packet.
|
||||
TTL uint8
|
||||
|
||||
// Protocol is the "protocol" field of an IPv4 packet.
|
||||
Protocol uint8
|
||||
|
||||
// checksum is the "checksum" field of an IPv4 packet.
|
||||
checksum uint16
|
||||
|
||||
// SrcAddr is the "source ip address" of an IPv4 packet.
|
||||
SrcAddr net.IP
|
||||
|
||||
// DstAddr is the "destination ip address" of an IPv4 packet.
|
||||
DstAddr net.IP
|
||||
}
|
||||
|
||||
// ipv4 represents an ipv4 header stored in a byte array.
|
||||
// Most of the methods of IPv4 access to the underlying slice without
|
||||
// checking the boundaries and could panic because of 'index out of range'.
|
||||
// Always call IsValid() to validate an instance of IPv4 before using other methods.
|
||||
type ipv4 []byte
|
||||
|
||||
const (
|
||||
// ipv4MinimumSize is the minimum size of a valid IPv4 packet.
|
||||
ipv4MinimumSize = 20
|
||||
|
||||
// ipv4MaximumHeaderSize is the maximum size of an IPv4 header. Given
|
||||
// that there are only 4 bits to represents the header length in 32-bit
|
||||
// units, the header cannot exceed 15*4 = 60 bytes.
|
||||
ipv4MaximumHeaderSize = 60
|
||||
|
||||
// ipv4AddressSize is the size, in bytes, of an IPv4 address.
|
||||
ipv4AddressSize = 4
|
||||
|
||||
// IPv4Version is the version of the IPv4 protocol.
|
||||
ipv4Version = 4
|
||||
)
|
||||
|
||||
// ipVersion returns the version of IP used in the given packet. It returns -1
|
||||
// if the packet is not large enough to contain the version field.
|
||||
func ipVersion(b []byte) int {
|
||||
// Length must be at least offset+length of version field.
|
||||
if len(b) < versIHL+1 {
|
||||
return -1
|
||||
}
|
||||
return int(b[versIHL] >> ipVersionShift)
|
||||
}
|
||||
|
||||
const (
|
||||
ipVersionShift = 4
|
||||
)
|
||||
|
||||
// headerLength returns the value of the "header length" field of the ipv4
|
||||
// header.
|
||||
func (b ipv4) headerLength() uint8 {
|
||||
return (b[versIHL] & 0xf) * 4
|
||||
}
|
||||
|
||||
// protocol returns the value of the protocol field of the ipv4 header.
|
||||
func (b ipv4) protocol() uint8 {
|
||||
return b[protocol]
|
||||
}
|
||||
|
||||
// sourceAddress returns the "source address" field of the ipv4 header.
|
||||
func (b ipv4) sourceAddress() net.IP {
|
||||
return net.IP(b[srcAddr : srcAddr+ipv4AddressSize])
|
||||
}
|
||||
|
||||
// destinationAddress returns the "destination address" field of the ipv4
|
||||
// header.
|
||||
func (b ipv4) destinationAddress() net.IP {
|
||||
return net.IP(b[dstAddr : dstAddr+ipv4AddressSize])
|
||||
}
|
||||
|
||||
// transportProtocol implements Network.transportProtocol.
|
||||
func (b ipv4) transportProtocol() transportProtocolNumber {
|
||||
return transportProtocolNumber(b.protocol())
|
||||
}
|
||||
|
||||
// payloadLength returns the length of the payload portion of the ipv4 packet.
|
||||
func (b ipv4) payloadLength() uint16 {
|
||||
return b.totalLength() - uint16(b.headerLength())
|
||||
}
|
||||
|
||||
// totalLength returns the "total length" field of the ipv4 header.
|
||||
func (b ipv4) totalLength() uint16 {
|
||||
return binary.BigEndian.Uint16(b[totalLen:])
|
||||
}
|
||||
|
||||
// setTotalLength sets the "total length" field of the ipv4 header.
|
||||
func (b ipv4) setTotalLength(totalLength uint16) {
|
||||
binary.BigEndian.PutUint16(b[totalLen:], totalLength)
|
||||
}
|
||||
|
||||
// setChecksum sets the checksum field of the ipv4 header.
|
||||
func (b ipv4) setChecksum(v uint16) {
|
||||
binary.BigEndian.PutUint16(b[checksumOff:], v)
|
||||
}
|
||||
|
||||
// setFlagsFragmentOffset sets the "flags" and "fragment offset" fields of the
|
||||
// ipv4 header.
|
||||
func (b ipv4) setFlagsFragmentOffset(flags uint8, offset uint16) {
|
||||
v := (uint16(flags) << 13) | (offset >> 3)
|
||||
binary.BigEndian.PutUint16(b[flagsFO:], v)
|
||||
}
|
||||
|
||||
// calculateChecksum calculates the checksum of the ipv4 header.
|
||||
func (b ipv4) calculateChecksum() uint16 {
|
||||
return checksum(b[:b.headerLength()], 0)
|
||||
}
|
||||
|
||||
// encode encodes all the fields of the ipv4 header.
|
||||
func (b ipv4) encode(i *ipv4Fields) {
|
||||
b[versIHL] = (4 << 4) | ((i.IHL / 4) & 0xf)
|
||||
b[tos] = i.TOS
|
||||
b.setTotalLength(i.TotalLength)
|
||||
binary.BigEndian.PutUint16(b[id:], i.ID)
|
||||
b.setFlagsFragmentOffset(i.Flags, i.FragmentOffset)
|
||||
b[ttl] = i.TTL
|
||||
b[protocol] = i.Protocol
|
||||
b.setChecksum(i.checksum)
|
||||
copy(b[srcAddr:srcAddr+ipv4AddressSize], i.SrcAddr)
|
||||
copy(b[dstAddr:dstAddr+ipv4AddressSize], i.DstAddr)
|
||||
}
|
||||
|
||||
// isValid performs basic validation on the packet.
|
||||
func (b ipv4) isValid(pktSize int) bool {
|
||||
if len(b) < ipv4MinimumSize {
|
||||
return false
|
||||
}
|
||||
|
||||
hlen := int(b.headerLength())
|
||||
tlen := int(b.totalLength())
|
||||
if hlen < ipv4MinimumSize || hlen > tlen || tlen > pktSize {
|
||||
return false
|
||||
}
|
||||
|
||||
if ipVersion(b) != ipv4Version {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const (
|
||||
udpSrcPort = 0
|
||||
udpDstPort = 2
|
||||
udpLength = 4
|
||||
udpchecksum = 6
|
||||
)
|
||||
|
||||
// udpFields contains the fields of a udp packet. It is used to describe the
|
||||
// fields of a packet that needs to be encoded.
|
||||
type udpFields struct {
|
||||
// SrcPort is the "source port" field of a udp packet.
|
||||
SrcPort uint16
|
||||
|
||||
// DstPort is the "destination port" field of a UDP packet.
|
||||
DstPort uint16
|
||||
|
||||
// Length is the "length" field of a UDP packet.
|
||||
Length uint16
|
||||
|
||||
// checksum is the "checksum" field of a UDP packet.
|
||||
checksum uint16
|
||||
}
|
||||
|
||||
// udp represents a udp header stored in a byte array.
|
||||
type udp []byte
|
||||
|
||||
const (
|
||||
// udpMinimumSize is the minimum size of a valid udp packet.
|
||||
udpMinimumSize = 8
|
||||
|
||||
// udpProtocolNumber is udp's transport protocol number.
|
||||
udpProtocolNumber transportProtocolNumber = 17
|
||||
)
|
||||
|
||||
// sourcePort returns the "source port" field of the udp header.
|
||||
func (b udp) sourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpSrcPort:])
|
||||
}
|
||||
|
||||
// DestinationPort returns the "destination port" field of the udp header.
|
||||
func (b udp) destinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpDstPort:])
|
||||
}
|
||||
|
||||
// Length returns the "length" field of the udp header.
|
||||
func (b udp) length() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpLength:])
|
||||
}
|
||||
|
||||
// setChecksum sets the "checksum" field of the udp header.
|
||||
func (b udp) setChecksum(checksum uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpchecksum:], checksum)
|
||||
}
|
||||
|
||||
// calculateChecksum calculates the checksum of the udp packet, given the total
|
||||
// length of the packet and the checksum of the network-layer pseudo-header
|
||||
// (excluding the total length) and the checksum of the payload.
|
||||
func (b udp) calculateChecksum(partialchecksum uint16, totalLen uint16) uint16 {
|
||||
// Add the length portion of the checksum to the pseudo-checksum.
|
||||
tmp := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(tmp, totalLen)
|
||||
xsum := checksum(tmp, partialchecksum)
|
||||
|
||||
// Calculate the rest of the checksum.
|
||||
return checksum(b[:udpMinimumSize], xsum)
|
||||
}
|
||||
|
||||
// encode encodes all the fields of the udp header.
|
||||
func (b udp) encode(u *udpFields) {
|
||||
binary.BigEndian.PutUint16(b[udpSrcPort:], u.SrcPort)
|
||||
binary.BigEndian.PutUint16(b[udpDstPort:], u.DstPort)
|
||||
binary.BigEndian.PutUint16(b[udpLength:], u.Length)
|
||||
binary.BigEndian.PutUint16(b[udpchecksum:], u.checksum)
|
||||
}
|
||||
|
||||
func calculateChecksum(buf []byte, initial uint32) uint16 {
|
||||
v := initial
|
||||
|
||||
l := len(buf)
|
||||
if l&1 != 0 {
|
||||
l--
|
||||
v += uint32(buf[l]) << 8
|
||||
}
|
||||
|
||||
for i := 0; i < l; i += 2 {
|
||||
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
|
||||
}
|
||||
|
||||
return checksumCombine(uint16(v), uint16(v>>16))
|
||||
}
|
||||
|
||||
// checksum calculates the checksum (as defined in RFC 1071) of the bytes in the
|
||||
// given byte array.
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func checksum(buf []byte, initial uint16) uint16 {
|
||||
return calculateChecksum(buf, uint32(initial))
|
||||
}
|
||||
|
||||
// checksumCombine combines the two uint16 to form their checksum. This is done
|
||||
// by adding them and the carry.
|
||||
//
|
||||
// Note that checksum a must have been computed on an even number of bytes.
|
||||
func checksumCombine(a, b uint16) uint16 {
|
||||
v := uint32(a) + uint32(b)
|
||||
return uint16(v + v>>16)
|
||||
}
|
||||
|
||||
// pseudoHeaderchecksum calculates the pseudo-header checksum for the
|
||||
// given destination protocol and network address, ignoring the length
|
||||
// field. pseudo-headers are needed by transport layers when calculating
|
||||
// their own checksum.
|
||||
func pseudoHeaderchecksum(protocol transportProtocolNumber, srcAddr net.IP, dstAddr net.IP) uint16 {
|
||||
xsum := checksum([]byte(srcAddr), 0)
|
||||
xsum = checksum([]byte(dstAddr), xsum)
|
||||
return checksum([]byte{0, uint8(protocol)}, xsum)
|
||||
}
|
||||
|
||||
func udp4pkt(packet []byte, dest *net.UDPAddr, src *net.UDPAddr) []byte {
|
||||
ipLen := ipv4MinimumSize
|
||||
udpLen := udpMinimumSize
|
||||
|
||||
h := make([]byte, 0, ipLen+udpLen+len(packet))
|
||||
hdr := uio.NewBigEndianBuffer(h)
|
||||
|
||||
ipv4fields := &ipv4Fields{
|
||||
IHL: ipv4MinimumSize,
|
||||
TotalLength: uint16(ipLen + udpLen + len(packet)),
|
||||
TTL: 64, // Per RFC 1700's recommendation for IP time to live
|
||||
Protocol: uint8(udpProtocolNumber),
|
||||
SrcAddr: src.IP.To4(),
|
||||
DstAddr: dest.IP.To4(),
|
||||
}
|
||||
ipv4hdr := ipv4(hdr.WriteN(ipLen))
|
||||
ipv4hdr.encode(ipv4fields)
|
||||
ipv4hdr.setChecksum(^ipv4hdr.calculateChecksum())
|
||||
|
||||
udphdr := udp(hdr.WriteN(udpLen))
|
||||
udphdr.encode(&udpFields{
|
||||
SrcPort: uint16(src.Port),
|
||||
DstPort: uint16(dest.Port),
|
||||
Length: uint16(udpLen + len(packet)),
|
||||
})
|
||||
|
||||
xsum := checksum(packet, pseudoHeaderchecksum(
|
||||
ipv4hdr.transportProtocol(), ipv4fields.SrcAddr, ipv4fields.DstAddr))
|
||||
udphdr.setChecksum(^udphdr.calculateChecksum(xsum, udphdr.length()))
|
||||
|
||||
hdr.WriteBytes(packet)
|
||||
return hdr.Data()
|
||||
}
|
79
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/lease.go
generated
vendored
Normal file
79
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/lease.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
// This is lease support for nclient4
|
||||
|
||||
package nclient4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
)
|
||||
|
||||
// Lease contains a DHCPv4 lease after DORA.
|
||||
// note: Lease doesn't include binding interface name
|
||||
type Lease struct {
|
||||
Offer *dhcpv4.DHCPv4
|
||||
ACK *dhcpv4.DHCPv4
|
||||
CreationTime time.Time
|
||||
}
|
||||
|
||||
// Release send DHCPv4 release messsage to server, based on specified lease.
|
||||
// release is sent as unicast per RFC2131, section 4.4.4.
|
||||
// Note: some DHCP server requries of using assigned IP address as source IP,
|
||||
// use nclient4.WithUnicast to create client for such case.
|
||||
func (c *Client) Release(lease *Lease, modifiers ...dhcpv4.Modifier) error {
|
||||
if lease == nil {
|
||||
return fmt.Errorf("lease is nil")
|
||||
}
|
||||
req, err := dhcpv4.NewReleaseFromACK(lease.ACK, modifiers...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to create release request,%w", err)
|
||||
}
|
||||
_, err = c.conn.WriteTo(req.ToBytes(), &net.UDPAddr{IP: lease.ACK.Options.Get(dhcpv4.OptionServerIdentifier), Port: ServerPort})
|
||||
if err == nil {
|
||||
c.logger.PrintMessage("sent message:", req)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Renew sends a DHCPv4 request to the server to renew the given lease. The renewal information is
|
||||
// sourced from the initial offer in the lease, and the ACK of the lease is updated to the ACK of
|
||||
// the latest renewal. This avoids issues with DHCP servers that omit information needed to build a
|
||||
// completely new lease from their renewal ACK (such as the Windows DHCP Server).
|
||||
func (c *Client) Renew(ctx context.Context, lease *Lease, modifiers ...dhcpv4.Modifier) (*Lease, error) {
|
||||
if lease == nil {
|
||||
return nil, fmt.Errorf("lease is nil")
|
||||
}
|
||||
|
||||
request, err := dhcpv4.NewRenewFromAck(lease.ACK, dhcpv4.PrependModifiers(modifiers,
|
||||
dhcpv4.WithOption(dhcpv4.OptMaxMessageSize(MaxMessageSize)))...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a request: %w", err)
|
||||
}
|
||||
|
||||
// Servers are supposed to only respond to Requests containing their server identifier,
|
||||
// but sometimes non-compliant servers respond anyway.
|
||||
// Clients are not required to validate this field, but servers are required to
|
||||
// include the server identifier in their Offer per RFC 2131 Section 4.3.1 Table 3.
|
||||
response, err := c.SendAndRead(ctx, c.serverAddr, request, IsAll(
|
||||
IsCorrectServer(lease.Offer.ServerIdentifier()),
|
||||
IsMessageType(dhcpv4.MessageTypeAck, dhcpv4.MessageTypeNak)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got an error while processing the request: %w", err)
|
||||
}
|
||||
if response.MessageType() == dhcpv4.MessageTypeNak {
|
||||
return nil, &ErrNak{
|
||||
Offer: lease.Offer,
|
||||
Nak: response,
|
||||
}
|
||||
}
|
||||
|
||||
// Return a new lease with the latest ACK and updated creation time
|
||||
return &Lease{
|
||||
Offer: lease.Offer,
|
||||
ACK: response,
|
||||
CreationTime: time.Now(),
|
||||
}, nil
|
||||
}
|
61
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_autoconfigure.go
generated
vendored
Normal file
61
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_autoconfigure.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AutoConfiguration implements encoding and decoding functions for a
|
||||
// byte enumeration as used in RFC 2563, Section 2.
|
||||
type AutoConfiguration byte
|
||||
|
||||
const (
|
||||
DoNotAutoConfigure AutoConfiguration = 0
|
||||
AutoConfigure AutoConfiguration = 1
|
||||
)
|
||||
|
||||
var autoConfigureToString = map[AutoConfiguration]string{
|
||||
DoNotAutoConfigure: "DoNotAutoConfigure",
|
||||
AutoConfigure: "AutoConfigure",
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (o AutoConfiguration) ToBytes() []byte {
|
||||
return []byte{byte(o)}
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (o AutoConfiguration) String() string {
|
||||
s := autoConfigureToString[o]
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("UNKNOWN (%d)", byte(o))
|
||||
}
|
||||
|
||||
// FromBytes parses a a single byte into AutoConfiguration
|
||||
func (o *AutoConfiguration) FromBytes(data []byte) error {
|
||||
if len(data) == 1 {
|
||||
*o = AutoConfiguration(data[0])
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid buffer length (%d)", len(data))
|
||||
}
|
||||
|
||||
// GetByte parses any single-byte option
|
||||
func GetByte(code OptionCode, o Options) (byte, error) {
|
||||
data := o.Get(code)
|
||||
if data == nil {
|
||||
return 0, fmt.Errorf("option not present")
|
||||
}
|
||||
if len(data) != 1 {
|
||||
return 0, fmt.Errorf("Invalid buffer length (%d)", len(data))
|
||||
}
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
// OptAutoConfigure returns a new AutoConfigure option.
|
||||
//
|
||||
// The AutoConfigure option is described by RFC 2563, Section 2.
|
||||
func OptAutoConfigure(autoconf AutoConfiguration) Option {
|
||||
return Option{Code: OptionAutoConfigure, Value: autoconf}
|
||||
}
|
56
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_duration.go
generated
vendored
Normal file
56
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_duration.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// MaxLeaseTime is the maximum lease time that can be encoded.
|
||||
var MaxLeaseTime = math.MaxUint32 * time.Second
|
||||
|
||||
// Duration implements the IP address lease time option described by RFC 2132,
|
||||
// Section 9.2.
|
||||
type Duration time.Duration
|
||||
|
||||
// FromBytes parses a duration from a byte stream according to RFC 2132, Section 9.2.
|
||||
func (d *Duration) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*d = Duration(time.Duration(buf.Read32()) * time.Second)
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (d Duration) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
buf.Write32(uint32(time.Duration(d) / time.Second))
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (d Duration) String() string {
|
||||
return time.Duration(d).String()
|
||||
}
|
||||
|
||||
// OptIPAddressLeaseTime returns a new IP address lease time option.
|
||||
//
|
||||
// The IP address lease time option is described by RFC 2132, Section 9.2.
|
||||
func OptIPAddressLeaseTime(d time.Duration) Option {
|
||||
return Option{Code: OptionIPAddressLeaseTime, Value: Duration(d)}
|
||||
}
|
||||
|
||||
// The IP address renew time option as described by RFC 2132, Section 9.11.
|
||||
func OptRenewTimeValue(d time.Duration) Option {
|
||||
return Option{Code: OptionRenewTimeValue, Value: Duration(d)}
|
||||
}
|
||||
|
||||
// The IP address rebinding time option as described by RFC 2132, Section 9.12.
|
||||
func OptRebindingTimeValue(d time.Duration) Option {
|
||||
return Option{Code: OptionRebindingTimeValue, Value: Duration(d)}
|
||||
}
|
||||
|
||||
// The IPv6-Only Preferred option is described by RFC 8925, Section 3.1
|
||||
func OptIPv6OnlyPreferred(d time.Duration) Option {
|
||||
return Option{Code: OptionIPv6OnlyPreferred, Value: Duration(d)}
|
||||
}
|
27
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_generic.go
generated
vendored
Normal file
27
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_generic.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// OptionGeneric is an option that only contains the option code and associated
|
||||
// data. Every option that does not have a specific implementation will fall
|
||||
// back to this option.
|
||||
type OptionGeneric struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized generic option as a slice of bytes.
|
||||
func (o OptionGeneric) ToBytes() []byte {
|
||||
return o.Data
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a generic option.
|
||||
func (o OptionGeneric) String() string {
|
||||
return fmt.Sprintf("%v", o.Data)
|
||||
}
|
||||
|
||||
// OptGeneric returns a generic option.
|
||||
func OptGeneric(code OptionCode, value []byte) Option {
|
||||
return Option{Code: code, Value: OptionGeneric{value}}
|
||||
}
|
62
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ip.go
generated
vendored
Normal file
62
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ip.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// IP implements DHCPv4 IP option marshaling and unmarshaling as described by
|
||||
// RFC 2132, Sections 5.3, 9.1, 9.7, and others.
|
||||
type IP net.IP
|
||||
|
||||
// FromBytes parses an IP from data in binary form.
|
||||
func (i *IP) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*i = IP(buf.CopyN(net.IPv4len))
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (i IP) ToBytes() []byte {
|
||||
return []byte(net.IP(i).To4())
|
||||
}
|
||||
|
||||
// String returns a human-readable IP.
|
||||
func (i IP) String() string {
|
||||
return net.IP(i).String()
|
||||
}
|
||||
|
||||
// GetIP returns code out of o parsed as an IP.
|
||||
func GetIP(code OptionCode, o Options) net.IP {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var ip IP
|
||||
if err := ip.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return net.IP(ip)
|
||||
}
|
||||
|
||||
// OptBroadcastAddress returns a new DHCPv4 Broadcast Address option.
|
||||
//
|
||||
// The broadcast address option is described in RFC 2132, Section 5.3.
|
||||
func OptBroadcastAddress(ip net.IP) Option {
|
||||
return Option{Code: OptionBroadcastAddress, Value: IP(ip)}
|
||||
}
|
||||
|
||||
// OptRequestedIPAddress returns a new DHCPv4 Requested IP Address option.
|
||||
//
|
||||
// The requested IP address option is described by RFC 2132, Section 9.1.
|
||||
func OptRequestedIPAddress(ip net.IP) Option {
|
||||
return Option{Code: OptionRequestedIPAddress, Value: IP(ip)}
|
||||
}
|
||||
|
||||
// OptServerIdentifier returns a new DHCPv4 Server Identifier option.
|
||||
//
|
||||
// The server identifier option is described by RFC 2132, Section 9.7.
|
||||
func OptServerIdentifier(ip net.IP) Option {
|
||||
return Option{Code: OptionServerIdentifier, Value: IP(ip)}
|
||||
}
|
103
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ips.go
generated
vendored
Normal file
103
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ips.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// IPs are IPv4 addresses from a DHCP packet as used and specified by options
|
||||
// in RFC 2132, Sections 3.5 through 3.13, 8.2, 8.3, 8.5, 8.6, 8.9, and 8.10.
|
||||
//
|
||||
// IPs implements the OptionValue type.
|
||||
type IPs []net.IP
|
||||
|
||||
// FromBytes parses an IPv4 address from a DHCP packet as used and specified by
|
||||
// options in RFC 2132, Sections 3.5 through 3.13, 8.2, 8.3, 8.5, 8.6, 8.9, and
|
||||
// 8.10.
|
||||
func (i *IPs) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
if buf.Len() == 0 {
|
||||
return fmt.Errorf("IP DHCP options must always list at least one IP")
|
||||
}
|
||||
|
||||
*i = make(IPs, 0, buf.Len()/net.IPv4len)
|
||||
for buf.Has(net.IPv4len) {
|
||||
*i = append(*i, net.IP(buf.CopyN(net.IPv4len)))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes marshals IPv4 addresses to a DHCP packet as specified by RFC 2132,
|
||||
// Section 3.5 et al.
|
||||
func (i IPs) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, ip := range i {
|
||||
buf.WriteBytes(ip.To4())
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a list of IPs.
|
||||
func (i IPs) String() string {
|
||||
s := make([]string, 0, len(i))
|
||||
for _, ip := range i {
|
||||
s = append(s, ip.String())
|
||||
}
|
||||
return strings.Join(s, ", ")
|
||||
}
|
||||
|
||||
// GetIPs parses a list of IPs from code in o.
|
||||
func GetIPs(code OptionCode, o Options) []net.IP {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var ips IPs
|
||||
if err := ips.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return []net.IP(ips)
|
||||
}
|
||||
|
||||
// OptRouter returns a new DHCPv4 Router option.
|
||||
//
|
||||
// The Router option is described by RFC 2132, Section 3.5.
|
||||
func OptRouter(routers ...net.IP) Option {
|
||||
return Option{
|
||||
Code: OptionRouter,
|
||||
Value: IPs(routers),
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter updates a packet with the DHCPv4 Router option.
|
||||
func WithRouter(routers ...net.IP) Modifier {
|
||||
return WithOption(OptRouter(routers...))
|
||||
}
|
||||
|
||||
// OptNTPServers returns a new DHCPv4 NTP Server option.
|
||||
//
|
||||
// The NTP servers option is described by RFC 2132, Section 8.3.
|
||||
func OptNTPServers(ntpServers ...net.IP) Option {
|
||||
return Option{
|
||||
Code: OptionNTPServers,
|
||||
Value: IPs(ntpServers),
|
||||
}
|
||||
}
|
||||
|
||||
// OptDNS returns a new DHCPv4 Domain Name Server option.
|
||||
//
|
||||
// The DNS server option is described by RFC 2132, Section 3.8.
|
||||
func OptDNS(servers ...net.IP) Option {
|
||||
return Option{
|
||||
Code: OptionDomainNameServer,
|
||||
Value: IPs(servers),
|
||||
}
|
||||
}
|
||||
|
||||
// WithDNS modifies a packet with the DHCPv4 Domain Name Server option.
|
||||
func WithDNS(servers ...net.IP) Modifier {
|
||||
return WithOption(OptDNS(servers...))
|
||||
}
|
50
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_maximum_dhcp_message_size.go
generated
vendored
Normal file
50
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_maximum_dhcp_message_size.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Uint16 implements encoding and decoding functions for a uint16 as used in
|
||||
// RFC 2132, Section 9.10.
|
||||
type Uint16 uint16
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (o Uint16) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
buf.Write16(uint16(o))
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (o Uint16) String() string {
|
||||
return fmt.Sprintf("%d", uint16(o))
|
||||
}
|
||||
|
||||
// FromBytes decodes data into o as per RFC 2132, Section 9.10.
|
||||
func (o *Uint16) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*o = Uint16(buf.Read16())
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// GetUint16 parses a uint16 from code in o.
|
||||
func GetUint16(code OptionCode, o Options) (uint16, error) {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return 0, fmt.Errorf("option not present")
|
||||
}
|
||||
var u Uint16
|
||||
if err := u.FromBytes(v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint16(u), nil
|
||||
}
|
||||
|
||||
// OptMaxMessageSize returns a new DHCP Maximum Message Size option.
|
||||
//
|
||||
// The Maximum DHCP Message Size option is described by RFC 2132, Section 9.10.
|
||||
func OptMaxMessageSize(size uint16) Option {
|
||||
return Option{Code: OptionMaximumDHCPMessageSize, Value: Uint16(size)}
|
||||
}
|
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_message_type.go
generated
vendored
Normal file
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_message_type.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
package dhcpv4
|
||||
|
||||
// OptMessageType returns a new DHCPv4 Message Type option.
|
||||
func OptMessageType(m MessageType) Option {
|
||||
return Option{Code: OptionDHCPMessageType, Value: m}
|
||||
}
|
23
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_misc.go
generated
vendored
Normal file
23
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_misc.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
)
|
||||
|
||||
// OptDomainSearch returns a new domain search option.
|
||||
//
|
||||
// The domain search option is described by RFC 3397, Section 2.
|
||||
func OptDomainSearch(labels *rfc1035label.Labels) Option {
|
||||
return Option{Code: OptionDNSDomainSearchList, Value: labels}
|
||||
}
|
||||
|
||||
// OptClientArch returns a new Client System Architecture Type option.
|
||||
func OptClientArch(archs ...iana.Arch) Option {
|
||||
return Option{Code: OptionClientSystemArchitectureType, Value: iana.Archs(archs)}
|
||||
}
|
||||
|
||||
// OptClientIdentifier returns a new Client Identifier option.
|
||||
func OptClientIdentifier(ident []byte) Option {
|
||||
return OptGeneric(OptionClientIdentifier, ident)
|
||||
}
|
72
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_parameter_request_list.go
generated
vendored
Normal file
72
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_parameter_request_list.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// OptionCodeList is a list of DHCP option codes.
|
||||
type OptionCodeList []OptionCode
|
||||
|
||||
// Has returns whether c is in the list.
|
||||
func (ol OptionCodeList) Has(c OptionCode) bool {
|
||||
for _, code := range ol {
|
||||
if code == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Add adds option codes in cs to ol.
|
||||
func (ol *OptionCodeList) Add(cs ...OptionCode) {
|
||||
for _, c := range cs {
|
||||
if !ol.Has(c) {
|
||||
*ol = append(*ol, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ol OptionCodeList) sort() {
|
||||
sort.Slice(ol, func(i, j int) bool { return ol[i].Code() < ol[j].Code() })
|
||||
}
|
||||
|
||||
// String returns a human-readable string for the option names.
|
||||
func (ol OptionCodeList) String() string {
|
||||
var names []string
|
||||
ol.sort()
|
||||
for _, code := range ol {
|
||||
names = append(names, code.String())
|
||||
}
|
||||
return strings.Join(names, ", ")
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option as defined by
|
||||
// RFC 2132, Section 9.8.
|
||||
func (ol OptionCodeList) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, req := range ol {
|
||||
buf.Write8(req.Code())
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// FromBytes parses a byte stream for this option as described by RFC 2132,
|
||||
// Section 9.8.
|
||||
func (ol *OptionCodeList) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*ol = make(OptionCodeList, 0, buf.Len())
|
||||
for buf.Has(1) {
|
||||
*ol = append(*ol, optionCode(buf.Read8()))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// OptParameterRequestList returns a new DHCPv4 Parameter Request List.
|
||||
//
|
||||
// The parameter request list option is described by RFC 2132, Section 9.8.
|
||||
func OptParameterRequestList(codes ...OptionCode) Option {
|
||||
return Option{Code: OptionParameterRequestList, Value: OptionCodeList(codes)}
|
||||
}
|
100
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_relay_agent_information.go
generated
vendored
Normal file
100
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_relay_agent_information.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RelayOptions is like Options, but stringifies using the Relay Agent Specific
|
||||
// option space.
|
||||
type RelayOptions struct {
|
||||
Options
|
||||
}
|
||||
|
||||
var relayHumanizer = OptionHumanizer{
|
||||
ValueHumanizer: func(code OptionCode, data []byte) fmt.Stringer {
|
||||
var d OptionDecoder
|
||||
switch code {
|
||||
case LinkSelectionSubOption, ServerIdentifierOverrideSubOption:
|
||||
d = &IPs{}
|
||||
}
|
||||
if d != nil && d.FromBytes(data) == nil {
|
||||
return d
|
||||
}
|
||||
return raiSubOptionValue{data}
|
||||
},
|
||||
CodeHumanizer: func(c uint8) OptionCode {
|
||||
return raiSubOptionCode(c)
|
||||
},
|
||||
}
|
||||
|
||||
// String prints the contained options using Relay Agent-specific option code parsing.
|
||||
func (r RelayOptions) String() string {
|
||||
return "\n" + r.Options.ToString(relayHumanizer)
|
||||
}
|
||||
|
||||
// FromBytes parses relay agent options from data.
|
||||
func (r *RelayOptions) FromBytes(data []byte) error {
|
||||
r.Options = make(Options)
|
||||
return r.Options.FromBytes(data)
|
||||
}
|
||||
|
||||
// OptRelayAgentInfo returns a new DHCP Relay Agent Info option.
|
||||
//
|
||||
// The relay agent info option is described by RFC 3046.
|
||||
func OptRelayAgentInfo(o ...Option) Option {
|
||||
return Option{Code: OptionRelayAgentInformation, Value: RelayOptions{OptionsFromList(o...)}}
|
||||
}
|
||||
|
||||
type raiSubOptionValue struct {
|
||||
val []byte
|
||||
}
|
||||
|
||||
func (rv raiSubOptionValue) String() string {
|
||||
return fmt.Sprintf("%q (%v)", string(rv.val), rv.val)
|
||||
}
|
||||
|
||||
type raiSubOptionCode uint8
|
||||
|
||||
func (o raiSubOptionCode) Code() uint8 {
|
||||
return uint8(o)
|
||||
}
|
||||
|
||||
func (o raiSubOptionCode) String() string {
|
||||
if s, ok := raiSubOptionCodeToString[o]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", o)
|
||||
}
|
||||
|
||||
// Option 82 Relay Agention Information Sub Options
|
||||
const (
|
||||
AgentCircuitIDSubOption raiSubOptionCode = 1 // RFC 3046
|
||||
AgentRemoteIDSubOption raiSubOptionCode = 2 // RFC 3046
|
||||
DOCSISDeviceClassSubOption raiSubOptionCode = 4 // RFC 3256
|
||||
LinkSelectionSubOption raiSubOptionCode = 5 // RFC 3527
|
||||
SubscriberIDSubOption raiSubOptionCode = 6 // RFC 3993
|
||||
RADIUSAttributesSubOption raiSubOptionCode = 7 // RFC 4014
|
||||
AuthenticationSubOption raiSubOptionCode = 8 // RFC 4030
|
||||
VendorSpecificInformationSubOption raiSubOptionCode = 9 // RFC 4243
|
||||
RelayAgentFlagsSubOption raiSubOptionCode = 10 // RFC 5010
|
||||
ServerIdentifierOverrideSubOption raiSubOptionCode = 11 // RFC 5107
|
||||
RelaySourcePortSubOption raiSubOptionCode = 19 // RFC 8357
|
||||
VirtualSubnetSelectionSubOption raiSubOptionCode = 151 // RFC 6607
|
||||
VirtualSubnetSelectionControlSubOption raiSubOptionCode = 152 // RFC 6607
|
||||
)
|
||||
|
||||
var raiSubOptionCodeToString = map[raiSubOptionCode]string{
|
||||
AgentCircuitIDSubOption: "Agent Circuit ID Sub-option",
|
||||
AgentRemoteIDSubOption: "Agent Remote ID Sub-option",
|
||||
DOCSISDeviceClassSubOption: "DOCSIS Device Class Sub-option",
|
||||
LinkSelectionSubOption: "Link Selection Sub-option",
|
||||
SubscriberIDSubOption: "Subscriber ID Sub-option",
|
||||
RADIUSAttributesSubOption: "RADIUS Attributes Sub-option",
|
||||
AuthenticationSubOption: "Authentication Sub-option",
|
||||
VendorSpecificInformationSubOption: "Vendor Specific Sub-option",
|
||||
RelayAgentFlagsSubOption: "Relay Agent Flags Sub-option",
|
||||
ServerIdentifierOverrideSubOption: "Server Identifier Override Sub-option",
|
||||
RelaySourcePortSubOption: "Relay Source Port Sub-option",
|
||||
VirtualSubnetSelectionSubOption: "Virtual Subnet Selection Sub-option",
|
||||
VirtualSubnetSelectionControlSubOption: "Virtual Subnet Selection Control Sub-option",
|
||||
}
|
104
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_routes.go
generated
vendored
Normal file
104
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_routes.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Route is a classless static route as per RFC 3442.
|
||||
type Route struct {
|
||||
// Dest is the destination network.
|
||||
Dest *net.IPNet
|
||||
|
||||
// Router is the router to use for the given destination network.
|
||||
Router net.IP
|
||||
}
|
||||
|
||||
// Marshal implements uio.Marshaler.
|
||||
//
|
||||
// Format described in RFC 3442:
|
||||
//
|
||||
// <size of mask in number of bits>
|
||||
// <destination address, omitting octets that must be zero per mask>
|
||||
// <route IP>
|
||||
func (r Route) Marshal(buf *uio.Lexer) {
|
||||
ones, _ := r.Dest.Mask.Size()
|
||||
buf.Write8(uint8(ones))
|
||||
|
||||
// Only write the non-zero octets.
|
||||
dstLen := (ones + 7) / 8
|
||||
buf.WriteBytes(r.Dest.IP.To4()[:dstLen])
|
||||
|
||||
buf.WriteBytes(r.Router.To4())
|
||||
}
|
||||
|
||||
// Unmarshal implements uio.Unmarshaler.
|
||||
func (r *Route) Unmarshal(buf *uio.Lexer) error {
|
||||
maskSize := buf.Read8()
|
||||
if maskSize > 32 {
|
||||
return fmt.Errorf("invalid mask length %d in route option", maskSize)
|
||||
}
|
||||
r.Dest = &net.IPNet{
|
||||
IP: make([]byte, net.IPv4len),
|
||||
Mask: net.CIDRMask(int(maskSize), 32),
|
||||
}
|
||||
|
||||
dstLen := (maskSize + 7) / 8
|
||||
buf.ReadBytes(r.Dest.IP[:dstLen])
|
||||
|
||||
r.Router = buf.CopyN(net.IPv4len)
|
||||
return buf.Error()
|
||||
}
|
||||
|
||||
// String prints the destination network and router IP.
|
||||
func (r *Route) String() string {
|
||||
return fmt.Sprintf("route to %s via %s", r.Dest, r.Router)
|
||||
}
|
||||
|
||||
// Routes is a collection of network routes.
|
||||
type Routes []*Route
|
||||
|
||||
// FromBytes parses routes from a set of bytes as described by RFC 3442.
|
||||
func (r *Routes) FromBytes(p []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(p)
|
||||
for buf.Has(1) {
|
||||
var route Route
|
||||
if err := route.Unmarshal(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = append(*r, &route)
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes marshals a set of routes as described by RFC 3442.
|
||||
func (r Routes) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, route := range r {
|
||||
route.Marshal(buf)
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String prints all routes.
|
||||
func (r Routes) String() string {
|
||||
s := make([]string, 0, len(r))
|
||||
for _, route := range r {
|
||||
s = append(s, route.String())
|
||||
}
|
||||
return strings.Join(s, "; ")
|
||||
}
|
||||
|
||||
// OptClasslessStaticRoute returns a new DHCPv4 Classless Static Route
|
||||
// option.
|
||||
//
|
||||
// The Classless Static Route option is described by RFC 3442.
|
||||
func OptClasslessStaticRoute(routes ...*Route) Option {
|
||||
return Option{
|
||||
Code: OptionClasslessStaticRoute,
|
||||
Value: Routes(routes),
|
||||
}
|
||||
}
|
84
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_string.go
generated
vendored
Normal file
84
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_string.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
package dhcpv4
|
||||
|
||||
// String represents an option encapsulating a string in IPv4 DHCP.
|
||||
//
|
||||
// This representation is shared by multiple options specified by RFC 2132,
|
||||
// Sections 3.14, 3.16, 3.17, 3.19, and 3.20.
|
||||
type String string
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (o String) ToBytes() []byte {
|
||||
return []byte(o)
|
||||
}
|
||||
|
||||
// String returns a human-readable string.
|
||||
func (o String) String() string {
|
||||
return string(o)
|
||||
}
|
||||
|
||||
// FromBytes parses a serialized stream of bytes into o.
|
||||
func (o *String) FromBytes(data []byte) error {
|
||||
*o = String(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetString parses an RFC 2132 string from o[code].
|
||||
func GetString(code OptionCode, o Options) string {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// OptDomainName returns a new DHCPv4 Domain Name option.
|
||||
//
|
||||
// The Domain Name option is described by RFC 2132, Section 3.17.
|
||||
func OptDomainName(name string) Option {
|
||||
return Option{Code: OptionDomainName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptHostName returns a new DHCPv4 Host Name option.
|
||||
//
|
||||
// The Host Name option is described by RFC 2132, Section 3.14.
|
||||
func OptHostName(name string) Option {
|
||||
return Option{Code: OptionHostName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptRootPath returns a new DHCPv4 Root Path option.
|
||||
//
|
||||
// The Root Path option is described by RFC 2132, Section 3.19.
|
||||
func OptRootPath(name string) Option {
|
||||
return Option{Code: OptionRootPath, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptBootFileName returns a new DHCPv4 Boot File Name option.
|
||||
//
|
||||
// The Bootfile Name option is described by RFC 2132, Section 9.5.
|
||||
func OptBootFileName(name string) Option {
|
||||
return Option{Code: OptionBootfileName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptTFTPServerName returns a new DHCPv4 TFTP Server Name option.
|
||||
//
|
||||
// The TFTP Server Name option is described by RFC 2132, Section 9.4.
|
||||
func OptTFTPServerName(name string) Option {
|
||||
return Option{Code: OptionTFTPServerName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptClassIdentifier returns a new DHCPv4 Class Identifier option.
|
||||
//
|
||||
// The Vendor Class Identifier option is described by RFC 2132, Section 9.13.
|
||||
func OptClassIdentifier(name string) Option {
|
||||
return Option{Code: OptionClassIdentifier, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptUserClass returns a new DHCPv4 User Class option.
|
||||
func OptUserClass(name string) Option {
|
||||
return Option{Code: OptionUserClassInformation, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptMessage returns a new DHCPv4 (Error) Message option.
|
||||
func OptMessage(msg string) Option {
|
||||
return Option{Code: OptionMessage, Value: String(msg)}
|
||||
}
|
55
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_strings.go
generated
vendored
Normal file
55
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_strings.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Strings represents an option encapsulating a list of strings in IPv4 DHCP as
|
||||
// specified in RFC 3004
|
||||
//
|
||||
// Strings implements the OptionValue type.
|
||||
type Strings []string
|
||||
|
||||
// FromBytes parses Strings from a DHCP packet as specified by RFC 3004.
|
||||
func (o *Strings) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
if buf.Len() == 0 {
|
||||
return fmt.Errorf("Strings DHCP option must always list at least one String")
|
||||
}
|
||||
|
||||
*o = make(Strings, 0)
|
||||
for buf.Has(1) {
|
||||
ucLen := buf.Read8()
|
||||
if ucLen == 0 {
|
||||
return fmt.Errorf("DHCP Strings must have length greater than 0")
|
||||
}
|
||||
*o = append(*o, string(buf.CopyN(int(ucLen))))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes marshals Strings to a DHCP packet as specified by RFC 3004.
|
||||
func (o Strings) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, uc := range o {
|
||||
buf.Write8(uint8(len(uc)))
|
||||
buf.WriteBytes([]byte(uc))
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a list of Strings.
|
||||
func (o Strings) String() string {
|
||||
return strings.Join(o, ", ")
|
||||
}
|
||||
|
||||
// OptRFC3004UserClass returns a new user class option according to RFC 3004.
|
||||
func OptRFC3004UserClass(v []string) Option {
|
||||
return Option{
|
||||
Code: OptionUserClassInformation,
|
||||
Value: Strings(v),
|
||||
}
|
||||
}
|
40
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_subnet_mask.go
generated
vendored
Normal file
40
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_subnet_mask.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// IPMask represents an option encapsulating the subnet mask.
|
||||
//
|
||||
// This option implements the subnet mask option in RFC 2132, Section 3.3.
|
||||
type IPMask net.IPMask
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (im IPMask) ToBytes() []byte {
|
||||
if len(im) > net.IPv4len {
|
||||
return im[:net.IPv4len]
|
||||
}
|
||||
return im
|
||||
}
|
||||
|
||||
// String returns a human-readable string.
|
||||
func (im IPMask) String() string {
|
||||
return net.IPMask(im).String()
|
||||
}
|
||||
|
||||
// FromBytes parses im from data per RFC 2132.
|
||||
func (im *IPMask) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*im = IPMask(buf.CopyN(net.IPv4len))
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// OptSubnetMask returns a new DHCPv4 SubnetMask option per RFC 2132, Section 3.3.
|
||||
func OptSubnetMask(mask net.IPMask) Option {
|
||||
return Option{
|
||||
Code: OptionSubnetMask,
|
||||
Value: IPMask(mask),
|
||||
}
|
||||
}
|
65
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_vivc.go
generated
vendored
Normal file
65
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_vivc.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// VIVCIdentifier implements the vendor-identifying vendor class option
|
||||
// described by RFC 3925.
|
||||
type VIVCIdentifier struct {
|
||||
// EntID is the enterprise ID.
|
||||
EntID iana.EnterpriseID
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// OptVIVC returns a new vendor-identifying vendor class option.
|
||||
//
|
||||
// The option is described by RFC 3925.
|
||||
func OptVIVC(identifiers ...VIVCIdentifier) Option {
|
||||
return Option{
|
||||
Code: OptionVendorIdentifyingVendorClass,
|
||||
Value: VIVCIdentifiers(identifiers),
|
||||
}
|
||||
}
|
||||
|
||||
// VIVCIdentifiers implements encoding and decoding methods for a DHCP option
|
||||
// described in RFC 3925.
|
||||
type VIVCIdentifiers []VIVCIdentifier
|
||||
|
||||
// FromBytes parses data into ids per RFC 3925.
|
||||
func (ids *VIVCIdentifiers) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
for buf.Has(5) {
|
||||
entID := iana.EnterpriseID(buf.Read32())
|
||||
idLen := int(buf.Read8())
|
||||
*ids = append(*ids, VIVCIdentifier{EntID: entID, Data: buf.CopyN(idLen)})
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (ids VIVCIdentifiers) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, id := range ids {
|
||||
buf.Write32(uint32(id.EntID))
|
||||
buf.Write8(uint8(len(id.Data)))
|
||||
buf.WriteBytes(id.Data)
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (ids VIVCIdentifiers) String() string {
|
||||
if len(ids) == 0 {
|
||||
return ""
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
for _, id := range ids {
|
||||
fmt.Fprintf(&buf, " %d:'%s',", id.EntID, id.Data)
|
||||
}
|
||||
return buf.String()[1 : buf.Len()-1]
|
||||
}
|
380
vendor/github.com/insomniacslk/dhcp/dhcpv4/options.go
generated
vendored
Normal file
380
vendor/github.com/insomniacslk/dhcp/dhcpv4/options.go
generated
vendored
Normal file
@ -0,0 +1,380 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrShortByteStream is an error that is thrown any time a short byte stream is
|
||||
// detected during option parsing.
|
||||
ErrShortByteStream = errors.New("short byte stream")
|
||||
|
||||
// ErrZeroLengthByteStream is an error that is thrown any time a zero-length
|
||||
// byte stream is encountered.
|
||||
ErrZeroLengthByteStream = errors.New("zero-length byte stream")
|
||||
)
|
||||
|
||||
// OptionValue is an interface that all DHCP v4 options adhere to.
|
||||
type OptionValue interface {
|
||||
ToBytes() []byte
|
||||
String() string
|
||||
}
|
||||
|
||||
// Option is a DHCPv4 option and consists of a 1-byte option code and a value
|
||||
// stream of bytes.
|
||||
//
|
||||
// The value is to be interpreted based on the option code.
|
||||
type Option struct {
|
||||
Code OptionCode
|
||||
Value OptionValue
|
||||
}
|
||||
|
||||
// String returns a human-readable version of this option.
|
||||
func (o Option) String() string {
|
||||
v := o.Value.String()
|
||||
if strings.Contains(v, "\n") {
|
||||
return fmt.Sprintf("%s:\n%s", o.Code, v)
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", o.Code, v)
|
||||
}
|
||||
|
||||
// Options is a collection of options.
|
||||
type Options map[uint8][]byte
|
||||
|
||||
// OptionsFromList adds all given options to an options map.
|
||||
func OptionsFromList(o ...Option) Options {
|
||||
opts := make(Options)
|
||||
for _, opt := range o {
|
||||
opts.Update(opt)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// Get will attempt to get all options that match a DHCPv4 option
|
||||
// from its OptionCode. If the option was not found it will return an
|
||||
// empty list.
|
||||
//
|
||||
// According to RFC 3396, options that are specified more than once are
|
||||
// concatenated, and hence this should always just return one option. This
|
||||
// currently returns a list to be API compatible.
|
||||
func (o Options) Get(code OptionCode) []byte {
|
||||
return o[code.Code()]
|
||||
}
|
||||
|
||||
// Has checks whether o has the given opcode.
|
||||
func (o Options) Has(opcode OptionCode) bool {
|
||||
_, ok := o[opcode.Code()]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Del deletes the option matching the option code.
|
||||
func (o Options) Del(opcode OptionCode) {
|
||||
delete(o, opcode.Code())
|
||||
}
|
||||
|
||||
// Update updates the existing options with the passed option, adding it
|
||||
// at the end if not present already
|
||||
func (o Options) Update(option Option) {
|
||||
o[option.Code.Code()] = option.Value.ToBytes()
|
||||
}
|
||||
|
||||
// ToBytes makes Options usable as an OptionValue as well.
|
||||
//
|
||||
// Used in the case of vendor-specific and relay agent options.
|
||||
func (o Options) ToBytes() []byte {
|
||||
return uio.ToBigEndian(o)
|
||||
}
|
||||
|
||||
// FromBytes parses a sequence of bytes until the end and builds a list of
|
||||
// options from it.
|
||||
//
|
||||
// The sequence should not contain the DHCP magic cookie.
|
||||
//
|
||||
// Returns an error if any invalid option or length is found.
|
||||
func (o Options) FromBytes(data []byte) error {
|
||||
return o.fromBytesCheckEnd(data, false)
|
||||
}
|
||||
|
||||
const (
|
||||
optPad = 0
|
||||
optAgentInfo = 82
|
||||
optEnd = 255
|
||||
)
|
||||
|
||||
// FromBytesCheckEnd parses Options from byte sequences using the
|
||||
// parsing function that is passed in as a paremeter
|
||||
func (o Options) fromBytesCheckEnd(data []byte, checkEndOption bool) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
|
||||
var end bool
|
||||
for buf.Len() >= 1 {
|
||||
// 1 byte: option code
|
||||
// 1 byte: option length n
|
||||
// n bytes: data
|
||||
code := buf.Read8()
|
||||
|
||||
if code == optPad {
|
||||
continue
|
||||
} else if code == optEnd {
|
||||
end = true
|
||||
break
|
||||
}
|
||||
length := int(buf.Read8())
|
||||
|
||||
// N bytes: option data
|
||||
data := buf.Consume(length)
|
||||
if data == nil {
|
||||
return fmt.Errorf("error collecting options: %v", buf.Error())
|
||||
}
|
||||
data = data[:length:length]
|
||||
|
||||
// RFC 2131, Section 4.1 "Options may appear only once, [...].
|
||||
// The client concatenates the values of multiple instances of
|
||||
// the same option into a single parameter list for
|
||||
// configuration."
|
||||
//
|
||||
// See also RFC 3396 for concatenation order and options longer
|
||||
// than 255 bytes.
|
||||
o[code] = append(o[code], data...)
|
||||
}
|
||||
|
||||
// If we never read the End option, the sender of this packet screwed
|
||||
// up.
|
||||
if !end && checkEndOption {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortedKeys returns an ordered slice of option keys from the Options map, for
|
||||
// use in serializing options to binary.
|
||||
func (o Options) sortedKeys() []int {
|
||||
// Send all values for a given key
|
||||
var codes []int
|
||||
var hasOptAgentInfo, hasOptEnd bool
|
||||
for k := range o {
|
||||
// RFC 3046 section 2.1 states that option 82 SHALL come last (ignoring End).
|
||||
if k == optAgentInfo {
|
||||
hasOptAgentInfo = true
|
||||
continue
|
||||
}
|
||||
if k == optEnd {
|
||||
hasOptEnd = true
|
||||
continue
|
||||
}
|
||||
codes = append(codes, int(k))
|
||||
}
|
||||
|
||||
sort.Ints(codes)
|
||||
|
||||
if hasOptAgentInfo {
|
||||
codes = append(codes, optAgentInfo)
|
||||
}
|
||||
if hasOptEnd {
|
||||
codes = append(codes, optEnd)
|
||||
}
|
||||
return codes
|
||||
}
|
||||
|
||||
// Marshal writes options binary representations to b.
|
||||
func (o Options) Marshal(b *uio.Lexer) {
|
||||
for _, c := range o.sortedKeys() {
|
||||
code := uint8(c)
|
||||
// Even if the End option is in there, don't marshal it until
|
||||
// the end.
|
||||
// Don't write padding either, since the options are sorted
|
||||
// it would always be written first which isn't useful
|
||||
if code == optEnd || code == optPad {
|
||||
continue
|
||||
}
|
||||
|
||||
data := o[code]
|
||||
|
||||
// Ensure even 0-length options are written out
|
||||
if len(data) == 0 {
|
||||
b.Write8(code)
|
||||
b.Write8(0)
|
||||
continue
|
||||
}
|
||||
// RFC 3396: If more than 256 bytes of data are given, the
|
||||
// option is simply listed multiple times.
|
||||
for len(data) > 0 {
|
||||
// 1 byte: option code
|
||||
b.Write8(code)
|
||||
|
||||
n := len(data)
|
||||
if n > math.MaxUint8 {
|
||||
n = math.MaxUint8
|
||||
}
|
||||
|
||||
// 1 byte: option length
|
||||
b.Write8(uint8(n))
|
||||
|
||||
// N bytes: option data
|
||||
b.WriteBytes(data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String prints options using DHCP-specified option codes.
|
||||
func (o Options) String() string {
|
||||
return o.ToString(dhcpHumanizer)
|
||||
}
|
||||
|
||||
// Summary prints options in human-readable values.
|
||||
//
|
||||
// Summary uses vendorParser to interpret the OptionVendorSpecificInformation option.
|
||||
func (o Options) Summary(vendorDecoder OptionDecoder) string {
|
||||
return o.ToString(OptionHumanizer{
|
||||
ValueHumanizer: parserFor(vendorDecoder),
|
||||
CodeHumanizer: func(c uint8) OptionCode {
|
||||
return optionCode(c)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// OptionParser gives a human-legible interpretation of data for the given option code.
|
||||
type OptionParser func(code OptionCode, data []byte) fmt.Stringer
|
||||
|
||||
// OptionHumanizer is used to interpret a set of Options for their option code
|
||||
// name and values.
|
||||
//
|
||||
// There should be separate OptionHumanizers for each Option "space": DHCP,
|
||||
// BSDP, Relay Agent Info, and others.
|
||||
type OptionHumanizer struct {
|
||||
ValueHumanizer OptionParser
|
||||
CodeHumanizer func(code uint8) OptionCode
|
||||
}
|
||||
|
||||
// Stringify returns a human-readable interpretation of the option code and its
|
||||
// associated data.
|
||||
func (oh OptionHumanizer) Stringify(code uint8, data []byte) string {
|
||||
c := oh.CodeHumanizer(code)
|
||||
val := oh.ValueHumanizer(c, data)
|
||||
return fmt.Sprintf("%s: %s", c, val)
|
||||
}
|
||||
|
||||
// dhcpHumanizer humanizes the set of DHCP option codes.
|
||||
var dhcpHumanizer = OptionHumanizer{
|
||||
ValueHumanizer: parseOption,
|
||||
CodeHumanizer: func(c uint8) OptionCode {
|
||||
return optionCode(c)
|
||||
},
|
||||
}
|
||||
|
||||
// ToString uses parse to parse options into human-readable values.
|
||||
func (o Options) ToString(humanizer OptionHumanizer) string {
|
||||
var ret string
|
||||
for _, c := range o.sortedKeys() {
|
||||
code := uint8(c)
|
||||
v := o[code]
|
||||
optString := humanizer.Stringify(code, v)
|
||||
// If this option has sub structures, offset them accordingly.
|
||||
if strings.Contains(optString, "\n") {
|
||||
optString = strings.Replace(optString, "\n ", "\n ", -1)
|
||||
}
|
||||
ret += fmt.Sprintf(" %v\n", optString)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseOption(code OptionCode, data []byte) fmt.Stringer {
|
||||
return parserFor(nil)(code, data)
|
||||
}
|
||||
|
||||
func parserFor(vendorParser OptionDecoder) OptionParser {
|
||||
return func(code OptionCode, data []byte) fmt.Stringer {
|
||||
return getOption(code, data, vendorParser)
|
||||
}
|
||||
}
|
||||
|
||||
// OptionDecoder can decode a byte stream into a human-readable option.
|
||||
type OptionDecoder interface {
|
||||
fmt.Stringer
|
||||
FromBytes([]byte) error
|
||||
}
|
||||
|
||||
func getOption(code OptionCode, data []byte, vendorDecoder OptionDecoder) fmt.Stringer {
|
||||
var d OptionDecoder
|
||||
switch code {
|
||||
case OptionRouter, OptionDomainNameServer, OptionNTPServers, OptionServerIdentifier:
|
||||
d = &IPs{}
|
||||
|
||||
case OptionBroadcastAddress, OptionRequestedIPAddress:
|
||||
d = &IP{}
|
||||
|
||||
case OptionClientSystemArchitectureType:
|
||||
d = &iana.Archs{}
|
||||
|
||||
case OptionSubnetMask:
|
||||
d = &IPMask{}
|
||||
|
||||
case OptionDHCPMessageType:
|
||||
var mt MessageType
|
||||
d = &mt
|
||||
|
||||
case OptionParameterRequestList:
|
||||
d = &OptionCodeList{}
|
||||
|
||||
case OptionHostName, OptionDomainName, OptionRootPath,
|
||||
OptionClassIdentifier, OptionTFTPServerName, OptionBootfileName,
|
||||
OptionMessage, OptionReferenceToTZDatabase:
|
||||
var s String
|
||||
d = &s
|
||||
|
||||
case OptionRelayAgentInformation:
|
||||
d = &RelayOptions{}
|
||||
|
||||
case OptionDNSDomainSearchList:
|
||||
d = &rfc1035label.Labels{}
|
||||
|
||||
case OptionIPAddressLeaseTime, OptionRenewTimeValue,
|
||||
OptionRebindingTimeValue, OptionIPv6OnlyPreferred, OptionArpCacheTimeout,
|
||||
OptionTimeOffset:
|
||||
var dur Duration
|
||||
d = &dur
|
||||
|
||||
case OptionMaximumDHCPMessageSize:
|
||||
var u Uint16
|
||||
d = &u
|
||||
|
||||
case OptionUserClassInformation:
|
||||
var s Strings
|
||||
d = &s
|
||||
if s.FromBytes(data) != nil {
|
||||
var s String
|
||||
d = &s
|
||||
}
|
||||
|
||||
case OptionAutoConfigure:
|
||||
var a AutoConfiguration
|
||||
d = &a
|
||||
|
||||
case OptionVendorIdentifyingVendorClass:
|
||||
d = &VIVCIdentifiers{}
|
||||
|
||||
case OptionVendorSpecificInformation:
|
||||
d = vendorDecoder
|
||||
|
||||
case OptionClasslessStaticRoute:
|
||||
d = &Routes{}
|
||||
}
|
||||
if d != nil && d.FromBytes(data) == nil {
|
||||
return d
|
||||
}
|
||||
return OptionGeneric{data}
|
||||
}
|
467
vendor/github.com/insomniacslk/dhcp/dhcpv4/types.go
generated
vendored
Normal file
467
vendor/github.com/insomniacslk/dhcp/dhcpv4/types.go
generated
vendored
Normal file
@ -0,0 +1,467 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// values from http://www.networksorcery.com/enp/protocol/dhcp.htm and
|
||||
// http://www.networksorcery.com/enp/protocol/bootp/options.htm
|
||||
|
||||
// TransactionID represents a 4-byte DHCP transaction ID as defined in RFC 951,
|
||||
// Section 3.
|
||||
//
|
||||
// The TransactionID is used to match DHCP replies to their original request.
|
||||
type TransactionID [4]byte
|
||||
|
||||
// String prints a hex transaction ID.
|
||||
func (xid TransactionID) String() string {
|
||||
return fmt.Sprintf("0x%x", xid[:])
|
||||
}
|
||||
|
||||
// MessageType represents the possible DHCP message types - DISCOVER, OFFER, etc
|
||||
type MessageType byte
|
||||
|
||||
// DHCP message types
|
||||
const (
|
||||
// MessageTypeNone is not a real message type, it is used by certain
|
||||
// functions to signal that no explicit message type is requested
|
||||
MessageTypeNone MessageType = 0
|
||||
MessageTypeDiscover MessageType = 1
|
||||
MessageTypeOffer MessageType = 2
|
||||
MessageTypeRequest MessageType = 3
|
||||
MessageTypeDecline MessageType = 4
|
||||
MessageTypeAck MessageType = 5
|
||||
MessageTypeNak MessageType = 6
|
||||
MessageTypeRelease MessageType = 7
|
||||
MessageTypeInform MessageType = 8
|
||||
)
|
||||
|
||||
// ToBytes returns the serialized version of this option described by RFC 2132,
|
||||
// Section 9.6.
|
||||
func (m MessageType) ToBytes() []byte {
|
||||
return []byte{byte(m)}
|
||||
}
|
||||
|
||||
// String prints a human-readable message type name.
|
||||
func (m MessageType) String() string {
|
||||
if s, ok := messageTypeToString[m]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", byte(m))
|
||||
}
|
||||
|
||||
// FromBytes reads a message type from data as described by RFC 2132, Section
|
||||
// 9.6.
|
||||
func (m *MessageType) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*m = MessageType(buf.Read8())
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
var messageTypeToString = map[MessageType]string{
|
||||
MessageTypeDiscover: "DISCOVER",
|
||||
MessageTypeOffer: "OFFER",
|
||||
MessageTypeRequest: "REQUEST",
|
||||
MessageTypeDecline: "DECLINE",
|
||||
MessageTypeAck: "ACK",
|
||||
MessageTypeNak: "NAK",
|
||||
MessageTypeRelease: "RELEASE",
|
||||
MessageTypeInform: "INFORM",
|
||||
}
|
||||
|
||||
// OpcodeType represents a DHCPv4 opcode.
|
||||
type OpcodeType uint8
|
||||
|
||||
// constants that represent valid values for OpcodeType
|
||||
const (
|
||||
OpcodeBootRequest OpcodeType = 1
|
||||
OpcodeBootReply OpcodeType = 2
|
||||
)
|
||||
|
||||
func (o OpcodeType) String() string {
|
||||
if s, ok := opcodeToString[o]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", uint8(o))
|
||||
}
|
||||
|
||||
var opcodeToString = map[OpcodeType]string{
|
||||
OpcodeBootRequest: "BootRequest",
|
||||
OpcodeBootReply: "BootReply",
|
||||
}
|
||||
|
||||
// OptionCode is a single byte representing the code for a given Option.
|
||||
//
|
||||
// OptionCode is an interface purely to support different stringers on options
|
||||
// with the same Code value, as vendor-specific options use option codes that
|
||||
// have the same value, but mean a different thing.
|
||||
type OptionCode interface {
|
||||
// Code is the 1 byte option code for the wire.
|
||||
Code() uint8
|
||||
|
||||
// String returns the option's name.
|
||||
String() string
|
||||
}
|
||||
|
||||
// optionCode is a DHCP option code.
|
||||
type optionCode uint8
|
||||
|
||||
// Code implements OptionCode.Code.
|
||||
func (o optionCode) Code() uint8 {
|
||||
return uint8(o)
|
||||
}
|
||||
|
||||
// String returns an option name.
|
||||
func (o optionCode) String() string {
|
||||
if s, ok := optionCodeToString[o]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", uint8(o))
|
||||
}
|
||||
|
||||
// GenericOptionCode is an unnamed option code.
|
||||
type GenericOptionCode uint8
|
||||
|
||||
// Code implements OptionCode.Code.
|
||||
func (o GenericOptionCode) Code() uint8 {
|
||||
return uint8(o)
|
||||
}
|
||||
|
||||
// String returns the option's name.
|
||||
func (o GenericOptionCode) String() string {
|
||||
return fmt.Sprintf("unknown (%d)", uint8(o))
|
||||
}
|
||||
|
||||
// DHCPv4 Options
|
||||
const (
|
||||
OptionPad optionCode = 0
|
||||
OptionSubnetMask optionCode = 1
|
||||
OptionTimeOffset optionCode = 2
|
||||
OptionRouter optionCode = 3
|
||||
OptionTimeServer optionCode = 4
|
||||
OptionNameServer optionCode = 5
|
||||
OptionDomainNameServer optionCode = 6
|
||||
OptionLogServer optionCode = 7
|
||||
OptionQuoteServer optionCode = 8
|
||||
OptionLPRServer optionCode = 9
|
||||
OptionImpressServer optionCode = 10
|
||||
OptionResourceLocationServer optionCode = 11
|
||||
OptionHostName optionCode = 12
|
||||
OptionBootFileSize optionCode = 13
|
||||
OptionMeritDumpFile optionCode = 14
|
||||
OptionDomainName optionCode = 15
|
||||
OptionSwapServer optionCode = 16
|
||||
OptionRootPath optionCode = 17
|
||||
OptionExtensionsPath optionCode = 18
|
||||
OptionIPForwarding optionCode = 19
|
||||
OptionNonLocalSourceRouting optionCode = 20
|
||||
OptionPolicyFilter optionCode = 21
|
||||
OptionMaximumDatagramAssemblySize optionCode = 22
|
||||
OptionDefaultIPTTL optionCode = 23
|
||||
OptionPathMTUAgingTimeout optionCode = 24
|
||||
OptionPathMTUPlateauTable optionCode = 25
|
||||
OptionInterfaceMTU optionCode = 26
|
||||
OptionAllSubnetsAreLocal optionCode = 27
|
||||
OptionBroadcastAddress optionCode = 28
|
||||
OptionPerformMaskDiscovery optionCode = 29
|
||||
OptionMaskSupplier optionCode = 30
|
||||
OptionPerformRouterDiscovery optionCode = 31
|
||||
OptionRouterSolicitationAddress optionCode = 32
|
||||
OptionStaticRoutingTable optionCode = 33
|
||||
OptionTrailerEncapsulation optionCode = 34
|
||||
OptionArpCacheTimeout optionCode = 35
|
||||
OptionEthernetEncapsulation optionCode = 36
|
||||
OptionDefaulTCPTTL optionCode = 37
|
||||
OptionTCPKeepaliveInterval optionCode = 38
|
||||
OptionTCPKeepaliveGarbage optionCode = 39
|
||||
OptionNetworkInformationServiceDomain optionCode = 40
|
||||
OptionNetworkInformationServers optionCode = 41
|
||||
OptionNTPServers optionCode = 42
|
||||
OptionVendorSpecificInformation optionCode = 43
|
||||
OptionNetBIOSOverTCPIPNameServer optionCode = 44
|
||||
OptionNetBIOSOverTCPIPDatagramDistributionServer optionCode = 45
|
||||
OptionNetBIOSOverTCPIPNodeType optionCode = 46
|
||||
OptionNetBIOSOverTCPIPScope optionCode = 47
|
||||
OptionXWindowSystemFontServer optionCode = 48
|
||||
OptionXWindowSystemDisplayManger optionCode = 49
|
||||
OptionRequestedIPAddress optionCode = 50
|
||||
OptionIPAddressLeaseTime optionCode = 51
|
||||
OptionOptionOverload optionCode = 52
|
||||
OptionDHCPMessageType optionCode = 53
|
||||
OptionServerIdentifier optionCode = 54
|
||||
OptionParameterRequestList optionCode = 55
|
||||
OptionMessage optionCode = 56
|
||||
OptionMaximumDHCPMessageSize optionCode = 57
|
||||
OptionRenewTimeValue optionCode = 58
|
||||
OptionRebindingTimeValue optionCode = 59
|
||||
OptionClassIdentifier optionCode = 60
|
||||
OptionClientIdentifier optionCode = 61
|
||||
OptionNetWareIPDomainName optionCode = 62
|
||||
OptionNetWareIPInformation optionCode = 63
|
||||
OptionNetworkInformationServicePlusDomain optionCode = 64
|
||||
OptionNetworkInformationServicePlusServers optionCode = 65
|
||||
OptionTFTPServerName optionCode = 66
|
||||
OptionBootfileName optionCode = 67
|
||||
OptionMobileIPHomeAgent optionCode = 68
|
||||
OptionSimpleMailTransportProtocolServer optionCode = 69
|
||||
OptionPostOfficeProtocolServer optionCode = 70
|
||||
OptionNetworkNewsTransportProtocolServer optionCode = 71
|
||||
OptionDefaultWorldWideWebServer optionCode = 72
|
||||
OptionDefaultFingerServer optionCode = 73
|
||||
OptionDefaultInternetRelayChatServer optionCode = 74
|
||||
OptionStreetTalkServer optionCode = 75
|
||||
OptionStreetTalkDirectoryAssistanceServer optionCode = 76
|
||||
OptionUserClassInformation optionCode = 77
|
||||
OptionSLPDirectoryAgent optionCode = 78
|
||||
OptionSLPServiceScope optionCode = 79
|
||||
OptionRapidCommit optionCode = 80
|
||||
OptionFQDN optionCode = 81
|
||||
OptionRelayAgentInformation optionCode = 82
|
||||
OptionInternetStorageNameService optionCode = 83
|
||||
// Option 84 returned in RFC 3679
|
||||
OptionNDSServers optionCode = 85
|
||||
OptionNDSTreeName optionCode = 86
|
||||
OptionNDSContext optionCode = 87
|
||||
OptionBCMCSControllerDomainNameList optionCode = 88
|
||||
OptionBCMCSControllerIPv4AddressList optionCode = 89
|
||||
OptionAuthentication optionCode = 90
|
||||
OptionClientLastTransactionTime optionCode = 91
|
||||
OptionAssociatedIP optionCode = 92
|
||||
OptionClientSystemArchitectureType optionCode = 93
|
||||
OptionClientNetworkInterfaceIdentifier optionCode = 94
|
||||
OptionLDAP optionCode = 95
|
||||
// Option 96 returned in RFC 3679
|
||||
OptionClientMachineIdentifier optionCode = 97
|
||||
OptionOpenGroupUserAuthentication optionCode = 98
|
||||
OptionGeoConfCivic optionCode = 99
|
||||
OptionIEEE10031TZString optionCode = 100
|
||||
OptionReferenceToTZDatabase optionCode = 101
|
||||
// Option 108 returned in RFC 8925
|
||||
OptionIPv6OnlyPreferred optionCode = 108
|
||||
// Options 102-111 returned in RFC 3679
|
||||
OptionNetInfoParentServerAddress optionCode = 112
|
||||
OptionNetInfoParentServerTag optionCode = 113
|
||||
OptionURL optionCode = 114
|
||||
// Option 115 returned in RFC 3679
|
||||
OptionAutoConfigure optionCode = 116
|
||||
OptionNameServiceSearch optionCode = 117
|
||||
OptionSubnetSelection optionCode = 118
|
||||
OptionDNSDomainSearchList optionCode = 119
|
||||
OptionSIPServers optionCode = 120
|
||||
OptionClasslessStaticRoute optionCode = 121
|
||||
OptionCCC optionCode = 122
|
||||
OptionGeoConf optionCode = 123
|
||||
OptionVendorIdentifyingVendorClass optionCode = 124
|
||||
OptionVendorIdentifyingVendorSpecific optionCode = 125
|
||||
// Options 126-127 returned in RFC 3679
|
||||
OptionTFTPServerIPAddress optionCode = 128
|
||||
OptionCallServerIPAddress optionCode = 129
|
||||
OptionDiscriminationString optionCode = 130
|
||||
OptionRemoteStatisticsServerIPAddress optionCode = 131
|
||||
Option8021PVLANID optionCode = 132
|
||||
Option8021QL2Priority optionCode = 133
|
||||
OptionDiffservCodePoint optionCode = 134
|
||||
OptionHTTPProxyForPhoneSpecificApplications optionCode = 135
|
||||
OptionPANAAuthenticationAgent optionCode = 136
|
||||
OptionLoSTServer optionCode = 137
|
||||
OptionCAPWAPAccessControllerAddresses optionCode = 138
|
||||
OptionOPTIONIPv4AddressMoS optionCode = 139
|
||||
OptionOPTIONIPv4FQDNMoS optionCode = 140
|
||||
OptionSIPUAConfigurationServiceDomains optionCode = 141
|
||||
OptionOPTIONIPv4AddressANDSF optionCode = 142
|
||||
OptionOPTIONIPv6AddressANDSF optionCode = 143
|
||||
// Options 144-149 returned in RFC 3679
|
||||
OptionTFTPServerAddress optionCode = 150
|
||||
OptionStatusCode optionCode = 151
|
||||
OptionBaseTime optionCode = 152
|
||||
OptionStartTimeOfState optionCode = 153
|
||||
OptionQueryStartTime optionCode = 154
|
||||
OptionQueryEndTime optionCode = 155
|
||||
OptionDHCPState optionCode = 156
|
||||
OptionDataSource optionCode = 157
|
||||
// Options 158-174 returned in RFC 3679
|
||||
OptionEtherboot optionCode = 175
|
||||
OptionIPTelephone optionCode = 176
|
||||
OptionEtherbootPacketCableAndCableHome optionCode = 177
|
||||
// Options 178-207 returned in RFC 3679
|
||||
OptionPXELinuxMagicString optionCode = 208
|
||||
OptionPXELinuxConfigFile optionCode = 209
|
||||
OptionPXELinuxPathPrefix optionCode = 210
|
||||
OptionPXELinuxRebootTime optionCode = 211
|
||||
OptionOPTION6RD optionCode = 212
|
||||
OptionOPTIONv4AccessDomain optionCode = 213
|
||||
// Options 214-219 returned in RFC 3679
|
||||
OptionSubnetAllocation optionCode = 220
|
||||
OptionVirtualSubnetAllocation optionCode = 221
|
||||
// Options 222-223 returned in RFC 3679
|
||||
// Options 224-254 are reserved for private use
|
||||
OptionEnd optionCode = 255
|
||||
)
|
||||
|
||||
var optionCodeToString = map[OptionCode]string{
|
||||
OptionPad: "Pad",
|
||||
OptionSubnetMask: "Subnet Mask",
|
||||
OptionTimeOffset: "Time Offset",
|
||||
OptionRouter: "Router",
|
||||
OptionTimeServer: "Time Server",
|
||||
OptionNameServer: "Name Server",
|
||||
OptionDomainNameServer: "Domain Name Server",
|
||||
OptionLogServer: "Log Server",
|
||||
OptionQuoteServer: "Quote Server",
|
||||
OptionLPRServer: "LPR Server",
|
||||
OptionImpressServer: "Impress Server",
|
||||
OptionResourceLocationServer: "Resource Location Server",
|
||||
OptionHostName: "Host Name",
|
||||
OptionBootFileSize: "Boot File Size",
|
||||
OptionMeritDumpFile: "Merit Dump File",
|
||||
OptionDomainName: "Domain Name",
|
||||
OptionSwapServer: "Swap Server",
|
||||
OptionRootPath: "Root Path",
|
||||
OptionExtensionsPath: "Extensions Path",
|
||||
OptionIPForwarding: "IP Forwarding enable/disable",
|
||||
OptionNonLocalSourceRouting: "Non-local Source Routing enable/disable",
|
||||
OptionPolicyFilter: "Policy Filter",
|
||||
OptionMaximumDatagramAssemblySize: "Maximum Datagram Reassembly Size",
|
||||
OptionDefaultIPTTL: "Default IP Time-to-live",
|
||||
OptionPathMTUAgingTimeout: "Path MTU Aging Timeout",
|
||||
OptionPathMTUPlateauTable: "Path MTU Plateau Table",
|
||||
OptionInterfaceMTU: "Interface MTU",
|
||||
OptionAllSubnetsAreLocal: "All Subnets Are Local",
|
||||
OptionBroadcastAddress: "Broadcast Address",
|
||||
OptionPerformMaskDiscovery: "Perform Mask Discovery",
|
||||
OptionMaskSupplier: "Mask Supplier",
|
||||
OptionPerformRouterDiscovery: "Perform Router Discovery",
|
||||
OptionRouterSolicitationAddress: "Router Solicitation Address",
|
||||
OptionStaticRoutingTable: "Static Routing Table",
|
||||
OptionTrailerEncapsulation: "Trailer Encapsulation",
|
||||
OptionArpCacheTimeout: "ARP Cache Timeout",
|
||||
OptionEthernetEncapsulation: "Ethernet Encapsulation",
|
||||
OptionDefaulTCPTTL: "Default TCP TTL",
|
||||
OptionTCPKeepaliveInterval: "TCP Keepalive Interval",
|
||||
OptionTCPKeepaliveGarbage: "TCP Keepalive Garbage",
|
||||
OptionNetworkInformationServiceDomain: "Network Information Service Domain",
|
||||
OptionNetworkInformationServers: "Network Information Servers",
|
||||
OptionNTPServers: "NTP Servers",
|
||||
OptionVendorSpecificInformation: "Vendor Specific Information",
|
||||
OptionNetBIOSOverTCPIPNameServer: "NetBIOS over TCP/IP Name Server",
|
||||
OptionNetBIOSOverTCPIPDatagramDistributionServer: "NetBIOS over TCP/IP Datagram Distribution Server",
|
||||
OptionNetBIOSOverTCPIPNodeType: "NetBIOS over TCP/IP Node Type",
|
||||
OptionNetBIOSOverTCPIPScope: "NetBIOS over TCP/IP Scope",
|
||||
OptionXWindowSystemFontServer: "X Window System Font Server",
|
||||
OptionXWindowSystemDisplayManger: "X Window System Display Manager",
|
||||
OptionRequestedIPAddress: "Requested IP Address",
|
||||
OptionIPAddressLeaseTime: "IP Addresses Lease Time",
|
||||
OptionOptionOverload: "Option Overload",
|
||||
OptionDHCPMessageType: "DHCP Message Type",
|
||||
OptionServerIdentifier: "Server Identifier",
|
||||
OptionParameterRequestList: "Parameter Request List",
|
||||
OptionMessage: "Message",
|
||||
OptionMaximumDHCPMessageSize: "Maximum DHCP Message Size",
|
||||
OptionRenewTimeValue: "Renew Time Value",
|
||||
OptionRebindingTimeValue: "Rebinding Time Value",
|
||||
OptionClassIdentifier: "Class Identifier",
|
||||
OptionClientIdentifier: "Client identifier",
|
||||
OptionNetWareIPDomainName: "NetWare/IP Domain Name",
|
||||
OptionNetWareIPInformation: "NetWare/IP Information",
|
||||
OptionNetworkInformationServicePlusDomain: "Network Information Service+ Domain",
|
||||
OptionNetworkInformationServicePlusServers: "Network Information Service+ Servers",
|
||||
OptionTFTPServerName: "TFTP Server Name",
|
||||
OptionBootfileName: "Bootfile Name",
|
||||
OptionMobileIPHomeAgent: "Mobile IP Home Agent",
|
||||
OptionSimpleMailTransportProtocolServer: "SMTP Server",
|
||||
OptionPostOfficeProtocolServer: "POP Server",
|
||||
OptionNetworkNewsTransportProtocolServer: "NNTP Server",
|
||||
OptionDefaultWorldWideWebServer: "Default WWW Server",
|
||||
OptionDefaultFingerServer: "Default Finger Server",
|
||||
OptionDefaultInternetRelayChatServer: "Default IRC Server",
|
||||
OptionStreetTalkServer: "StreetTalk Server",
|
||||
OptionStreetTalkDirectoryAssistanceServer: "StreetTalk Directory Assistance Server",
|
||||
OptionUserClassInformation: "User Class Information",
|
||||
OptionSLPDirectoryAgent: "SLP DIrectory Agent",
|
||||
OptionSLPServiceScope: "SLP Service Scope",
|
||||
OptionRapidCommit: "Rapid Commit",
|
||||
OptionFQDN: "FQDN",
|
||||
OptionRelayAgentInformation: "Relay Agent Information",
|
||||
OptionInternetStorageNameService: "Internet Storage Name Service",
|
||||
// Option 84 returned in RFC 3679
|
||||
OptionNDSServers: "NDS Servers",
|
||||
OptionNDSTreeName: "NDS Tree Name",
|
||||
OptionNDSContext: "NDS Context",
|
||||
OptionBCMCSControllerDomainNameList: "BCMCS Controller Domain Name List",
|
||||
OptionBCMCSControllerIPv4AddressList: "BCMCS Controller IPv4 Address List",
|
||||
OptionAuthentication: "Authentication",
|
||||
OptionClientLastTransactionTime: "Client Last Transaction Time",
|
||||
OptionAssociatedIP: "Associated IP",
|
||||
OptionClientSystemArchitectureType: "Client System Architecture Type",
|
||||
OptionClientNetworkInterfaceIdentifier: "Client Network Interface Identifier",
|
||||
OptionLDAP: "LDAP",
|
||||
// Option 96 returned in RFC 3679
|
||||
OptionClientMachineIdentifier: "Client Machine Identifier",
|
||||
OptionOpenGroupUserAuthentication: "OpenGroup's User Authentication",
|
||||
OptionGeoConfCivic: "GEOCONF_CIVIC",
|
||||
OptionIEEE10031TZString: "IEEE 1003.1 TZ String",
|
||||
OptionReferenceToTZDatabase: "Reference to the TZ Database",
|
||||
// Option 108 returned in RFC 8925
|
||||
OptionIPv6OnlyPreferred: "IPv6-Only Preferred",
|
||||
// Options 102-111 returned in RFC 3679
|
||||
OptionNetInfoParentServerAddress: "NetInfo Parent Server Address",
|
||||
OptionNetInfoParentServerTag: "NetInfo Parent Server Tag",
|
||||
OptionURL: "URL",
|
||||
// Option 115 returned in RFC 3679
|
||||
OptionAutoConfigure: "Auto-Configure",
|
||||
OptionNameServiceSearch: "Name Service Search",
|
||||
OptionSubnetSelection: "Subnet Selection",
|
||||
OptionDNSDomainSearchList: "DNS Domain Search List",
|
||||
OptionSIPServers: "SIP Servers",
|
||||
OptionClasslessStaticRoute: "Classless Static Route",
|
||||
OptionCCC: "CCC, CableLabs Client Configuration",
|
||||
OptionGeoConf: "GeoConf",
|
||||
OptionVendorIdentifyingVendorClass: "Vendor-Identifying Vendor Class",
|
||||
OptionVendorIdentifyingVendorSpecific: "Vendor-Identifying Vendor-Specific",
|
||||
// Options 126-127 returned in RFC 3679
|
||||
OptionTFTPServerIPAddress: "TFTP Server IP Address",
|
||||
OptionCallServerIPAddress: "Call Server IP Address",
|
||||
OptionDiscriminationString: "Discrimination String",
|
||||
OptionRemoteStatisticsServerIPAddress: "RemoteStatistics Server IP Address",
|
||||
Option8021PVLANID: "802.1P VLAN ID",
|
||||
Option8021QL2Priority: "802.1Q L2 Priority",
|
||||
OptionDiffservCodePoint: "Diffserv Code Point",
|
||||
OptionHTTPProxyForPhoneSpecificApplications: "HTTP Proxy for phone-specific applications",
|
||||
OptionPANAAuthenticationAgent: "PANA Authentication Agent",
|
||||
OptionLoSTServer: "LoST Server",
|
||||
OptionCAPWAPAccessControllerAddresses: "CAPWAP Access Controller Addresses",
|
||||
OptionOPTIONIPv4AddressMoS: "OPTION-IPv4_Address-MoS",
|
||||
OptionOPTIONIPv4FQDNMoS: "OPTION-IPv4_FQDN-MoS",
|
||||
OptionSIPUAConfigurationServiceDomains: "SIP UA Configuration Service Domains",
|
||||
OptionOPTIONIPv4AddressANDSF: "OPTION-IPv4_Address-ANDSF",
|
||||
OptionOPTIONIPv6AddressANDSF: "OPTION-IPv6_Address-ANDSF",
|
||||
// Options 144-149 returned in RFC 3679
|
||||
OptionTFTPServerAddress: "TFTP Server Address",
|
||||
OptionStatusCode: "Status Code",
|
||||
OptionBaseTime: "Base Time",
|
||||
OptionStartTimeOfState: "Start Time of State",
|
||||
OptionQueryStartTime: "Query Start Time",
|
||||
OptionQueryEndTime: "Query End Time",
|
||||
OptionDHCPState: "DHCP Staet",
|
||||
OptionDataSource: "Data Source",
|
||||
// Options 158-174 returned in RFC 3679
|
||||
OptionEtherboot: "Etherboot",
|
||||
OptionIPTelephone: "IP Telephone",
|
||||
OptionEtherbootPacketCableAndCableHome: "Etherboot / PacketCable and CableHome",
|
||||
// Options 178-207 returned in RFC 3679
|
||||
OptionPXELinuxMagicString: "PXELinux Magic String",
|
||||
OptionPXELinuxConfigFile: "PXELinux Config File",
|
||||
OptionPXELinuxPathPrefix: "PXELinux Path Prefix",
|
||||
OptionPXELinuxRebootTime: "PXELinux Reboot Time",
|
||||
OptionOPTION6RD: "OPTION_6RD",
|
||||
OptionOPTIONv4AccessDomain: "OPTION_V4_ACCESS_DOMAIN",
|
||||
// Options 214-219 returned in RFC 3679
|
||||
OptionSubnetAllocation: "Subnet Allocation",
|
||||
OptionVirtualSubnetAllocation: "Virtual Subnet Selection",
|
||||
// Options 222-223 returned in RFC 3679
|
||||
// Options 224-254 are reserved for private use
|
||||
|
||||
OptionEnd: "End",
|
||||
}
|
148
vendor/github.com/insomniacslk/dhcp/iana/archtype.go
generated
vendored
Normal file
148
vendor/github.com/insomniacslk/dhcp/iana/archtype.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
package iana
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Arch encodes an architecture type per RFC 4578, Section 2.1.
|
||||
type Arch uint16
|
||||
|
||||
// See RFC 4578, 5970, and http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#processor-architecture
|
||||
const (
|
||||
INTEL_X86PC Arch = 0
|
||||
NEC_PC98 Arch = 1
|
||||
EFI_ITANIUM Arch = 2
|
||||
DEC_ALPHA Arch = 3
|
||||
ARC_X86 Arch = 4
|
||||
INTEL_LEAN_CLIENT Arch = 5
|
||||
EFI_IA32 Arch = 6
|
||||
EFI_X86_64 Arch = 7
|
||||
EFI_XSCALE Arch = 8
|
||||
EFI_BC Arch = 9
|
||||
EFI_ARM32 Arch = 10
|
||||
EFI_ARM64 Arch = 11
|
||||
PPC_OPEN_FIRMWARE Arch = 12
|
||||
PPC_EPAPR Arch = 13
|
||||
PPC_OPAL Arch = 14
|
||||
EFI_X86_HTTP Arch = 15
|
||||
EFI_X86_64_HTTP Arch = 16
|
||||
EFI_BC_HTTP Arch = 17
|
||||
EFI_ARM32_HTTP Arch = 18
|
||||
EFI_ARM64_HTTP Arch = 19
|
||||
INTEL_X86PC_HTTP Arch = 20
|
||||
UBOOT_ARM32 Arch = 21
|
||||
UBOOT_ARM64 Arch = 22
|
||||
UBOOT_ARM32_HTTP Arch = 23
|
||||
UBOOT_ARM64_HTTP Arch = 24
|
||||
EFI_RISCV32 Arch = 25
|
||||
EFI_RISCV32_HTTP Arch = 26
|
||||
EFI_RISCV64 Arch = 27
|
||||
EFI_RISCV64_HTTP Arch = 28
|
||||
EFI_RISCV128 Arch = 29
|
||||
EFI_RISCV128_HTTP Arch = 30
|
||||
S390_BASIC Arch = 31
|
||||
S390_EXTENDED Arch = 32
|
||||
EFI_MIPS32 Arch = 33
|
||||
EFI_MIPS64 Arch = 34
|
||||
EFI_SUNWAY32 Arch = 35
|
||||
EFI_SUNWAY64 Arch = 36
|
||||
)
|
||||
|
||||
// archTypeToStringMap maps an Arch to a mnemonic name
|
||||
var archTypeToStringMap = map[Arch]string{
|
||||
INTEL_X86PC: "Intel x86PC",
|
||||
NEC_PC98: "NEC/PC98",
|
||||
EFI_ITANIUM: "EFI Itanium",
|
||||
DEC_ALPHA: "DEC Alpha",
|
||||
ARC_X86: "Arc x86",
|
||||
INTEL_LEAN_CLIENT: "Intel Lean Client",
|
||||
EFI_IA32: "EFI IA32",
|
||||
EFI_XSCALE: "EFI Xscale",
|
||||
EFI_X86_64: "EFI x86-64",
|
||||
EFI_BC: "EFI BC",
|
||||
EFI_ARM32: "EFI ARM32",
|
||||
EFI_ARM64: "EFI ARM64",
|
||||
PPC_OPEN_FIRMWARE: "PowerPC Open Firmware",
|
||||
PPC_EPAPR: "PowerPC ePAPR",
|
||||
PPC_OPAL: "POWER OPAL v3",
|
||||
EFI_X86_HTTP: "EFI x86 boot from HTTP",
|
||||
EFI_X86_64_HTTP: "EFI x86-64 boot from HTTP",
|
||||
EFI_BC_HTTP: "EFI BC boot from HTTP",
|
||||
EFI_ARM32_HTTP: "EFI ARM32 boot from HTTP",
|
||||
EFI_ARM64_HTTP: "EFI ARM64 boot from HTTP",
|
||||
INTEL_X86PC_HTTP: "Intel x86PC boot from HTTP",
|
||||
UBOOT_ARM32: "U-Boot ARM32",
|
||||
UBOOT_ARM64: "U-Boot ARM64",
|
||||
UBOOT_ARM32_HTTP: "U-boot ARM32 boot from HTTP",
|
||||
UBOOT_ARM64_HTTP: "U-Boot ARM64 boot from HTTP",
|
||||
EFI_RISCV32: "EFI RISC-V 32-bit",
|
||||
EFI_RISCV32_HTTP: "EFI RISC-V 32-bit boot from HTTP",
|
||||
EFI_RISCV64: "EFI RISC-V 64-bit",
|
||||
EFI_RISCV64_HTTP: "EFI RISC-V 64-bit boot from HTTP",
|
||||
EFI_RISCV128: "EFI RISC-V 128-bit",
|
||||
EFI_RISCV128_HTTP: "EFI RISC-V 128-bit boot from HTTP",
|
||||
S390_BASIC: "s390 Basic",
|
||||
S390_EXTENDED: "s390 Extended",
|
||||
EFI_MIPS32: "EFI MIPS32",
|
||||
EFI_MIPS64: "EFI MIPS64",
|
||||
EFI_SUNWAY32: "EFI Sunway 32-bit",
|
||||
EFI_SUNWAY64: "EFI Sunway 64-bit",
|
||||
}
|
||||
|
||||
// String returns a mnemonic name for a given architecture type.
|
||||
func (a Arch) String() string {
|
||||
if at := archTypeToStringMap[a]; at != "" {
|
||||
return at
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// Archs represents multiple Arch values.
|
||||
type Archs []Arch
|
||||
|
||||
// Contains returns whether b is one of the Archs in a.
|
||||
func (a Archs) Contains(b Arch) bool {
|
||||
for _, t := range a {
|
||||
if t == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ToBytes returns the serialized option defined by RFC 4578 (DHCPv4) and RFC
|
||||
// 5970 (DHCPv6) as the Client System Architecture Option.
|
||||
func (a Archs) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, at := range a {
|
||||
buf.Write16(uint16(at))
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns the list of archs in a human-readable manner.
|
||||
func (a Archs) String() string {
|
||||
s := make([]string, 0, len(a))
|
||||
for _, arch := range a {
|
||||
s = append(s, arch.String())
|
||||
}
|
||||
return strings.Join(s, ", ")
|
||||
}
|
||||
|
||||
// FromBytes parses a DHCP list of architecture types as defined by RFC 4578
|
||||
// and RFC 5970.
|
||||
func (a *Archs) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
if buf.Len() == 0 {
|
||||
return fmt.Errorf("must have at least one archtype if option is present")
|
||||
}
|
||||
|
||||
*a = make([]Arch, 0, buf.Len()/2)
|
||||
for buf.Has(2) {
|
||||
*a = append(*a, Arch(buf.Read16()))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
25
vendor/github.com/insomniacslk/dhcp/iana/entid.go
generated
vendored
Normal file
25
vendor/github.com/insomniacslk/dhcp/iana/entid.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package iana
|
||||
|
||||
// EnterpriseID represents the Enterprise IDs as set by IANA
|
||||
type EnterpriseID int
|
||||
|
||||
// See https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers for values
|
||||
const (
|
||||
EnterpriseIDCiscoSystems EnterpriseID = 9
|
||||
EnterpriseIDCienaCorporation EnterpriseID = 1271
|
||||
EnterpriseIDMellanoxTechnologiesLTD EnterpriseID = 33049
|
||||
)
|
||||
|
||||
var enterpriseIDToStringMap = map[EnterpriseID]string{
|
||||
EnterpriseIDCiscoSystems: "Cisco Systems",
|
||||
EnterpriseIDCienaCorporation: "Ciena Corporation",
|
||||
EnterpriseIDMellanoxTechnologiesLTD: "Mellanox Technologies LTD",
|
||||
}
|
||||
|
||||
// String returns the vendor name for a given Enterprise ID
|
||||
func (e EnterpriseID) String() string {
|
||||
if vendor := enterpriseIDToStringMap[e]; vendor != "" {
|
||||
return vendor
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
91
vendor/github.com/insomniacslk/dhcp/iana/hwtypes.go
generated
vendored
Normal file
91
vendor/github.com/insomniacslk/dhcp/iana/hwtypes.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
package iana
|
||||
|
||||
// HWType is a hardware type as per RFC 2132 and defined by the IANA.
|
||||
type HWType uint16
|
||||
|
||||
// See IANA for values.
|
||||
const (
|
||||
_ HWType = iota // skip 0
|
||||
HWTypeEthernet
|
||||
HWTypeExperimentalEthernet
|
||||
HWTypeAmateurRadioAX25
|
||||
HWTypeProteonTokenRing
|
||||
HWTypeChaos
|
||||
HWTypeIEEE802
|
||||
HWTypeARCNET
|
||||
HWTypeHyperchannel
|
||||
HWTypeLanstar
|
||||
HWTypeAutonet
|
||||
HWTypeLocalTalk
|
||||
HWTypeLocalNet
|
||||
HWTypeUltraLink
|
||||
HWTypeSMDS
|
||||
HWTypeFrameRelay
|
||||
HWTypeATM
|
||||
HWTypeHDLC
|
||||
HWTypeFibreChannel
|
||||
HWTypeATM2
|
||||
HWTypeSerialLine
|
||||
HWTypeATM3
|
||||
HWTypeMILSTD188220
|
||||
HWTypeMetricom
|
||||
HWTypeIEEE1394
|
||||
HWTypeMAPOS
|
||||
HWTypeTwinaxial
|
||||
HWTypeEUI64
|
||||
HWTypeHIPARP
|
||||
HWTypeISO7816
|
||||
HWTypeARPSec
|
||||
HWTypeIPsec
|
||||
HWTypeInfiniband
|
||||
HWTypeCAI
|
||||
HWTypeWiegandInterface
|
||||
HWTypePureIP
|
||||
)
|
||||
|
||||
var hwTypeToString = map[HWType]string{
|
||||
HWTypeEthernet: "Ethernet",
|
||||
HWTypeExperimentalEthernet: "Experimental Ethernet",
|
||||
HWTypeAmateurRadioAX25: "Amateur Radio AX.25",
|
||||
HWTypeProteonTokenRing: "Proteon ProNET Token Ring",
|
||||
HWTypeChaos: "Chaos",
|
||||
HWTypeIEEE802: "IEEE 802",
|
||||
HWTypeARCNET: "ARCNET",
|
||||
HWTypeHyperchannel: "Hyperchannel",
|
||||
HWTypeLanstar: "Lanstar",
|
||||
HWTypeAutonet: "Autonet Short Address",
|
||||
HWTypeLocalTalk: "LocalTalk",
|
||||
HWTypeLocalNet: "LocalNet",
|
||||
HWTypeUltraLink: "Ultra link",
|
||||
HWTypeSMDS: "SMDS",
|
||||
HWTypeFrameRelay: "Frame Relay",
|
||||
HWTypeATM: "ATM",
|
||||
HWTypeHDLC: "HDLC",
|
||||
HWTypeFibreChannel: "Fibre Channel",
|
||||
HWTypeATM2: "ATM 2",
|
||||
HWTypeSerialLine: "Serial Line",
|
||||
HWTypeATM3: "ATM 3",
|
||||
HWTypeMILSTD188220: "MIL-STD-188-220",
|
||||
HWTypeMetricom: "Metricom",
|
||||
HWTypeIEEE1394: "IEEE 1394.1995",
|
||||
HWTypeMAPOS: "MAPOS",
|
||||
HWTypeTwinaxial: "Twinaxial",
|
||||
HWTypeEUI64: "EUI-64",
|
||||
HWTypeHIPARP: "HIPARP",
|
||||
HWTypeISO7816: "IP and ARP over ISO 7816-3",
|
||||
HWTypeARPSec: "ARPSec",
|
||||
HWTypeIPsec: "IPsec tunnel",
|
||||
HWTypeInfiniband: "Infiniband",
|
||||
HWTypeCAI: "CAI, TIA-102 Project 125 Common Air Interface",
|
||||
HWTypeWiegandInterface: "Wiegand Interface",
|
||||
HWTypePureIP: "Pure IP",
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (h HWType) String() string {
|
||||
hwtype := hwTypeToString[h]
|
||||
if hwtype == "" {
|
||||
hwtype = "unknown"
|
||||
}
|
||||
return hwtype
|
||||
}
|
2
vendor/github.com/insomniacslk/dhcp/iana/iana.go
generated
vendored
Normal file
2
vendor/github.com/insomniacslk/dhcp/iana/iana.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package iana contains constants defined by IANA.
|
||||
package iana
|
77
vendor/github.com/insomniacslk/dhcp/iana/statuscodes.go
generated
vendored
Normal file
77
vendor/github.com/insomniacslk/dhcp/iana/statuscodes.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package iana
|
||||
|
||||
// StatusCode represents a IANA status code for DHCPv6
|
||||
//
|
||||
// IANA Status Codes for DHCPv6
|
||||
// https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
|
||||
type StatusCode uint16
|
||||
|
||||
// IANA status codes
|
||||
const (
|
||||
// RFC 3315 par. 24..4
|
||||
StatusSuccess StatusCode = 0
|
||||
StatusUnspecFail StatusCode = 1
|
||||
StatusNoAddrsAvail StatusCode = 2
|
||||
StatusNoBinding StatusCode = 3
|
||||
StatusNotOnLink StatusCode = 4
|
||||
StatusUseMulticast StatusCode = 5
|
||||
StatusNoPrefixAvail StatusCode = 6
|
||||
// RFC 5007
|
||||
StatusUnknownQueryType StatusCode = 7
|
||||
StatusMalformedQuery StatusCode = 8
|
||||
StatusNotConfigured StatusCode = 9
|
||||
StatusNotAllowed StatusCode = 10
|
||||
// RFC 5460
|
||||
StatusQueryTerminated StatusCode = 11
|
||||
// RFC 7653
|
||||
StatusDataMissing StatusCode = 12
|
||||
StatusCatchUpComplete StatusCode = 13
|
||||
StatusNotSupported StatusCode = 14
|
||||
StatusTLSConnectionRefused StatusCode = 15
|
||||
// RFC 8156
|
||||
StatusAddressInUse StatusCode = 16
|
||||
StatusConfigurationConflict StatusCode = 17
|
||||
StatusMissingBindingInformation StatusCode = 18
|
||||
StatusOutdatedBindingInformation StatusCode = 19
|
||||
StatusServerShuttingDown StatusCode = 20
|
||||
StatusDNSUpdateNotSupported StatusCode = 21
|
||||
StatusExcessiveTimeSkew StatusCode = 22
|
||||
)
|
||||
|
||||
// String returns a mnemonic name for a given status code
|
||||
func (s StatusCode) String() string {
|
||||
if sc := statusCodeToStringMap[s]; sc != "" {
|
||||
return sc
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
var statusCodeToStringMap = map[StatusCode]string{
|
||||
StatusSuccess: "Success",
|
||||
StatusUnspecFail: "UnspecFail",
|
||||
StatusNoAddrsAvail: "NoAddrsAvail",
|
||||
StatusNoBinding: "NoBinding",
|
||||
StatusNotOnLink: "NotOnLink",
|
||||
StatusUseMulticast: "UseMulticast",
|
||||
StatusNoPrefixAvail: "NoPrefixAvail",
|
||||
// RFC 5007
|
||||
StatusUnknownQueryType: "UnknownQueryType",
|
||||
StatusMalformedQuery: "MalformedQuery",
|
||||
StatusNotConfigured: "NotConfigured",
|
||||
StatusNotAllowed: "NotAllowed",
|
||||
// RFC 5460
|
||||
StatusQueryTerminated: "QueryTerminated",
|
||||
// RFC 7653
|
||||
StatusDataMissing: "DataMissing",
|
||||
StatusCatchUpComplete: "CatchUpComplete",
|
||||
StatusNotSupported: "NotSupported",
|
||||
StatusTLSConnectionRefused: "TLSConnectionRefused",
|
||||
// RFC 8156
|
||||
StatusAddressInUse: "AddressInUse",
|
||||
StatusConfigurationConflict: "ConfigurationConflict",
|
||||
StatusMissingBindingInformation: "MissingBindingInformation",
|
||||
StatusOutdatedBindingInformation: "OutdatedBindingInformation",
|
||||
StatusServerShuttingDown: "ServerShuttingDown",
|
||||
StatusDNSUpdateNotSupported: "DNSUpdateNotSupported",
|
||||
StatusExcessiveTimeSkew: "ExcessiveTimeSkew",
|
||||
}
|
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_bsd.go
generated
vendored
Normal file
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_bsd.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// +build aix freebsd openbsd netbsd dragonfly
|
||||
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// BindToInterface emulates linux's SO_BINDTODEVICE option for a socket by using
|
||||
// IP_RECVIF.
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_RECVIF, iface.Index)
|
||||
}
|
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_darwin.go
generated
vendored
Normal file
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_darwin.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// +build darwin
|
||||
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// BindToInterface emulates linux's SO_BINDTODEVICE option for a socket by using
|
||||
// IP_BOUND_IF.
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index)
|
||||
}
|
9
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_linux.go
generated
vendored
Normal file
9
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_linux.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// +build linux
|
||||
|
||||
package interfaces
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
return unix.BindToDevice(fd, ifname)
|
||||
}
|
8
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_windows.go
generated
vendored
Normal file
8
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_windows.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package interfaces
|
||||
|
||||
import "errors"
|
||||
|
||||
// BindToInterface fails on Windows.
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
return errors.New("not implemented on Windows")
|
||||
}
|
41
vendor/github.com/insomniacslk/dhcp/interfaces/interfaces.go
generated
vendored
Normal file
41
vendor/github.com/insomniacslk/dhcp/interfaces/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package interfaces
|
||||
|
||||
import "net"
|
||||
|
||||
// InterfaceMatcher is a function type used to match the interfaces we want. See
|
||||
// GetInterfacesFunc below for usage.
|
||||
type InterfaceMatcher func(net.Interface) bool
|
||||
|
||||
// interfaceGetter is used for testing purposes
|
||||
var interfaceGetter = net.Interfaces
|
||||
|
||||
// GetInterfacesFunc loops through the available network interfaces, and returns
|
||||
// a list of interfaces for which the passed InterfaceMatcher function returns
|
||||
// true.
|
||||
func GetInterfacesFunc(matcher InterfaceMatcher) ([]net.Interface, error) {
|
||||
ifaces, err := interfaceGetter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]net.Interface, 0)
|
||||
for _, iface := range ifaces {
|
||||
if matcher(iface) {
|
||||
ret = append(ret, iface)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetLoopbackInterfaces returns a list of loopback interfaces.
|
||||
func GetLoopbackInterfaces() ([]net.Interface, error) {
|
||||
return GetInterfacesFunc(func(iface net.Interface) bool {
|
||||
return iface.Flags&net.FlagLoopback != 0
|
||||
})
|
||||
}
|
||||
|
||||
// GetNonLoopbackInterfaces returns a list of non-loopback interfaces.
|
||||
func GetNonLoopbackInterfaces() ([]net.Interface, error) {
|
||||
return GetInterfacesFunc(func(iface net.Interface) bool {
|
||||
return iface.Flags&net.FlagLoopback == 0
|
||||
})
|
||||
}
|
173
vendor/github.com/insomniacslk/dhcp/rfc1035label/label.go
generated
vendored
Normal file
173
vendor/github.com/insomniacslk/dhcp/rfc1035label/label.go
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
package rfc1035label
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Labels represents RFC1035 labels
|
||||
//
|
||||
// This implements RFC 1035 labels, including compression.
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.1.4
|
||||
type Labels struct {
|
||||
// original contains the original bytes if the object was parsed from a byte
|
||||
// sequence, or nil otherwise. The `original` field is necessary to deal
|
||||
// with compressed labels. If the labels are further modified, the original
|
||||
// content is invalidated and no compression will be used.
|
||||
original []byte
|
||||
// Labels contains the parsed labels. A change here invalidates the
|
||||
// `original` object.
|
||||
Labels []string
|
||||
}
|
||||
|
||||
// same compares two string arrays
|
||||
func same(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String prints labels.
|
||||
func (l *Labels) String() string {
|
||||
return fmt.Sprintf("%v", l.Labels)
|
||||
}
|
||||
|
||||
// ToBytes returns a byte sequence representing the labels. If the original
|
||||
// sequence is modified, the labels are parsed again, otherwise the original
|
||||
// byte sequence is returned.
|
||||
func (l *Labels) ToBytes() []byte {
|
||||
// if the original byte sequence has been modified, invalidate it and
|
||||
// serialize again.
|
||||
// NOTE: this function is not thread-safe. If multiple threads modify
|
||||
// the `Labels` field, the result may be wrong.
|
||||
originalLabels, err := labelsFromBytes(l.original)
|
||||
// if the original object has not been modified, or we cannot parse it,
|
||||
// return the original bytes.
|
||||
if err != nil || (l.original != nil && same(originalLabels, l.Labels)) {
|
||||
return l.original
|
||||
}
|
||||
return labelsToBytes(l.Labels)
|
||||
}
|
||||
|
||||
// Length returns the length in bytes of the serialized labels
|
||||
func (l *Labels) Length() int {
|
||||
return len(l.ToBytes())
|
||||
}
|
||||
|
||||
// NewLabels returns an initialized Labels object.
|
||||
func NewLabels() *Labels {
|
||||
return &Labels{
|
||||
Labels: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// FromBytes reads labels from a bytes stream according to RFC 1035.
|
||||
func (l *Labels) FromBytes(data []byte) error {
|
||||
labs, err := labelsFromBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.original = data
|
||||
l.Labels = labs
|
||||
return nil
|
||||
}
|
||||
|
||||
// FromBytes returns a Labels object from the given byte sequence, or an error if
|
||||
// any.
|
||||
func FromBytes(data []byte) (*Labels, error) {
|
||||
var l Labels
|
||||
if err := l.FromBytes(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
// ErrBufferTooShort is returned when the label cannot be parsed due to a wrong
|
||||
// length or missing bytes.
|
||||
var ErrBufferTooShort = errors.New("rfc1035label: buffer too short")
|
||||
|
||||
// fromBytes decodes a serialized stream and returns a list of labels
|
||||
func labelsFromBytes(buf []byte) ([]string, error) {
|
||||
var (
|
||||
labels = make([]string, 0)
|
||||
pos, oldPos int
|
||||
label string
|
||||
handlingPointer bool
|
||||
)
|
||||
|
||||
for {
|
||||
if pos >= len(buf) {
|
||||
// interpret label without trailing zero-length byte as a partial
|
||||
// domain name field as per RFC 4704 Section 4.2
|
||||
if label != "" {
|
||||
labels = append(labels, label)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
length := int(buf[pos])
|
||||
pos++
|
||||
var chunk string
|
||||
if length == 0 {
|
||||
labels = append(labels, label)
|
||||
label = ""
|
||||
if handlingPointer {
|
||||
pos = oldPos
|
||||
handlingPointer = false
|
||||
}
|
||||
} else if length&0xc0 == 0xc0 {
|
||||
// compression pointer
|
||||
if handlingPointer {
|
||||
return nil, errors.New("rfc1035label: cannot handle nested pointers")
|
||||
}
|
||||
handlingPointer = true
|
||||
if pos+1 > len(buf) {
|
||||
return nil, errors.New("rfc1035label: pointer buffer too short")
|
||||
}
|
||||
off := int(buf[pos-1]&^0xc0)<<8 + int(buf[pos])
|
||||
oldPos = pos + 1
|
||||
pos = off
|
||||
} else {
|
||||
if pos+length > len(buf) {
|
||||
return nil, ErrBufferTooShort
|
||||
}
|
||||
chunk = string(buf[pos : pos+length])
|
||||
if label != "" {
|
||||
label += "."
|
||||
}
|
||||
label += chunk
|
||||
pos += length
|
||||
}
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
// labelToBytes encodes a label and returns a serialized stream of bytes
|
||||
func labelToBytes(label string) []byte {
|
||||
var encodedLabel []byte
|
||||
if len(label) == 0 {
|
||||
return []byte{0}
|
||||
}
|
||||
for _, part := range strings.Split(label, ".") {
|
||||
encodedLabel = append(encodedLabel, byte(len(part)))
|
||||
encodedLabel = append(encodedLabel, []byte(part)...)
|
||||
}
|
||||
return append(encodedLabel, 0)
|
||||
}
|
||||
|
||||
// labelsToBytes encodes a list of labels and returns a serialized stream of
|
||||
// bytes
|
||||
func labelsToBytes(labels []string) []byte {
|
||||
var encodedLabels []byte
|
||||
for _, label := range labels {
|
||||
encodedLabels = append(encodedLabels, labelToBytes(label)...)
|
||||
}
|
||||
return encodedLabels
|
||||
}
|
8
vendor/github.com/josharian/native/doc.go
generated
vendored
Normal file
8
vendor/github.com/josharian/native/doc.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
// Package native provides easy access to native byte order.
|
||||
//
|
||||
// Usage: use native.Endian where you need the native binary.ByteOrder.
|
||||
//
|
||||
// Please think twice before using this package.
|
||||
// It can break program portability.
|
||||
// Native byte order is usually not the right answer.
|
||||
package native
|
14
vendor/github.com/josharian/native/endian_big.go
generated
vendored
Normal file
14
vendor/github.com/josharian/native/endian_big.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build mips || mips64 || ppc64 || s390x
|
||||
// +build mips mips64 ppc64 s390x
|
||||
|
||||
package native
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// Endian is the encoding/binary.ByteOrder implementation for the
|
||||
// current CPU's native byte order.
|
||||
var Endian = binary.BigEndian
|
||||
|
||||
// IsBigEndian is whether the current CPU's native byte order is big
|
||||
// endian.
|
||||
const IsBigEndian = true
|
31
vendor/github.com/josharian/native/endian_generic.go
generated
vendored
Normal file
31
vendor/github.com/josharian/native/endian_generic.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//go:build !mips && !mips64 && !ppc64 && !s390x && !amd64 && !386 && !arm && !arm64 && !loong64 && !mipsle && !mips64le && !ppc64le && !riscv64 && !wasm
|
||||
// +build !mips,!mips64,!ppc64,!s390x,!amd64,!386,!arm,!arm64,!loong64,!mipsle,!mips64le,!ppc64le,!riscv64,!wasm
|
||||
|
||||
// This file is a fallback, so that package native doesn't break
|
||||
// the instant the Go project adds support for a new architecture.
|
||||
//
|
||||
|
||||
package native
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var Endian binary.ByteOrder
|
||||
|
||||
var IsBigEndian bool
|
||||
|
||||
func init() {
|
||||
b := uint16(0xff) // one byte
|
||||
if *(*byte)(unsafe.Pointer(&b)) == 0 {
|
||||
Endian = binary.BigEndian
|
||||
IsBigEndian = true
|
||||
} else {
|
||||
Endian = binary.LittleEndian
|
||||
IsBigEndian = false
|
||||
}
|
||||
log.Printf("github.com/josharian/native: unrecognized arch %v (%v), please file an issue", runtime.GOARCH, Endian)
|
||||
}
|
14
vendor/github.com/josharian/native/endian_little.go
generated
vendored
Normal file
14
vendor/github.com/josharian/native/endian_little.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build amd64 || 386 || arm || arm64 || loong64 || mipsle || mips64le || ppc64le || riscv64 || wasm
|
||||
// +build amd64 386 arm arm64 loong64 mipsle mips64le ppc64le riscv64 wasm
|
||||
|
||||
package native
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// Endian is the encoding/binary.ByteOrder implementation for the
|
||||
// current CPU's native byte order.
|
||||
var Endian = binary.LittleEndian
|
||||
|
||||
// IsBigEndian is whether the current CPU's native byte order is big
|
||||
// endian.
|
||||
const IsBigEndian = false
|
7
vendor/github.com/josharian/native/license
generated
vendored
Normal file
7
vendor/github.com/josharian/native/license
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
Copyright 2020 Josh Bleecher Snyder
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
10
vendor/github.com/josharian/native/readme.md
generated
vendored
Normal file
10
vendor/github.com/josharian/native/readme.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
Package native provides easy access to native byte order.
|
||||
|
||||
`go get github.com/josharian/native`
|
||||
|
||||
Usage: Use `native.Endian` where you need the native binary.ByteOrder.
|
||||
|
||||
Please think twice before using this package.
|
||||
It can break program portability.
|
||||
Native byte order is usually not the right answer.
|
||||
|
1
vendor/github.com/mdlayher/packet/.gitignore
generated
vendored
Normal file
1
vendor/github.com/mdlayher/packet/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
cmd/packet
|
27
vendor/github.com/mdlayher/packet/CHANGELOG.md
generated
vendored
Normal file
27
vendor/github.com/mdlayher/packet/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# CHANGELOG
|
||||
|
||||
# v1.1.2
|
||||
|
||||
- [Improvement]: updated dependencies, test with Go 1.20.
|
||||
|
||||
# v1.1.1
|
||||
|
||||
- [Bug Fix]: fix test compilation on big endian machines.
|
||||
|
||||
# v1.1.0
|
||||
|
||||
**This is the first release of package packet that only supports Go 1.18+. Users
|
||||
on older versions of Go must use v1.0.0.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go so we can begin using
|
||||
modern versions of `x/sys` and other dependencies.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
**This is the last release of package vsock that supports Go 1.17 and below.**
|
||||
|
||||
- Initial stable commit! The API is mostly a direct translation of the previous
|
||||
`github.com/mdlayher/raw` package APIs, with some updates to make everything
|
||||
focused explicitly on Linux and `AF_PACKET` sockets. Functionally, the two
|
||||
packages are equivalent, and `*raw.Conn` is now backed by `*packet.Conn` in
|
||||
the latest version of the `raw` package.
|
9
vendor/github.com/mdlayher/packet/LICENSE.md
generated
vendored
Normal file
9
vendor/github.com/mdlayher/packet/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (C) 2022 Matt Layher
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
35
vendor/github.com/mdlayher/packet/README.md
generated
vendored
Normal file
35
vendor/github.com/mdlayher/packet/README.md
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
# packet [](https://github.com/mdlayher/packet/actions) [](https://pkg.go.dev/github.com/mdlayher/packet) [](https://goreportcard.com/report/github.com/mdlayher/packet)
|
||||
|
||||
Package `packet` provides access to Linux packet sockets (`AF_PACKET`). MIT
|
||||
Licensed.
|
||||
|
||||
## Stability
|
||||
|
||||
See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
|
||||
releases.
|
||||
|
||||
This package has a stable v1 API and any future breaking changes will prompt
|
||||
the release of a new major version. Features and bug fixes will continue to
|
||||
occur in the v1.x.x series.
|
||||
|
||||
This package only supports the two most recent major versions of Go, mirroring
|
||||
Go's own release policy. Older versions of Go may lack critical features and bug
|
||||
fixes which are necessary for this package to function correctly.
|
||||
|
||||
## History
|
||||
|
||||
One of my first major Go networking projects was
|
||||
[`github.com/mdlayher/raw`](https://github.com/mdlayher/raw), which provided
|
||||
access to Linux `AF_PACKET` sockets and *BSD equivalent mechanisms for sending
|
||||
and receiving Ethernet frames. However, the *BSD support languished and I lack
|
||||
the expertise and time to properly maintain code for operating systems I do not
|
||||
use on a daily basis.
|
||||
|
||||
Package `packet` is a successor to package `raw`, but exclusively focused on
|
||||
Linux and `AF_PACKET` sockets. The APIs are nearly identical, but with a few
|
||||
changes which take into account some of the lessons learned while working on
|
||||
`raw`.
|
||||
|
||||
Users are highly encouraged to migrate any existing Linux uses of `raw` to
|
||||
package `packet` instead. This package will be supported for the foreseeable
|
||||
future and will receive continued updates as necessary.
|
2
vendor/github.com/mdlayher/packet/doc.go
generated
vendored
Normal file
2
vendor/github.com/mdlayher/packet/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package packet provides access to Linux packet sockets (AF_PACKET).
|
||||
package packet
|
241
vendor/github.com/mdlayher/packet/packet.go
generated
vendored
Normal file
241
vendor/github.com/mdlayher/packet/packet.go
generated
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
const (
|
||||
// network is the network reported in net.OpError.
|
||||
network = "packet"
|
||||
|
||||
// Operation names which may be returned in net.OpError.
|
||||
opClose = "close"
|
||||
opGetsockopt = "getsockopt"
|
||||
opListen = "listen"
|
||||
opRawControl = "raw-control"
|
||||
opRawRead = "raw-read"
|
||||
opRawWrite = "raw-write"
|
||||
opRead = "read"
|
||||
opSet = "set"
|
||||
opSetsockopt = "setsockopt"
|
||||
opSyscallConn = "syscall-conn"
|
||||
opWrite = "write"
|
||||
)
|
||||
|
||||
// Config contains options for a Conn.
|
||||
type Config struct {
|
||||
// Filter is an optional assembled BPF filter which can be applied to the
|
||||
// Conn before bind(2) is called.
|
||||
//
|
||||
// The Conn.SetBPF method serves the same purpose once a Conn has already
|
||||
// been opened, but setting Filter applies the BPF filter before the Conn is
|
||||
// bound. This ensures that unexpected packets will not be captured before
|
||||
// the Conn is opened.
|
||||
Filter []bpf.RawInstruction
|
||||
}
|
||||
|
||||
// Type is a socket type used when creating a Conn with Listen.
|
||||
//enumcheck:exhaustive
|
||||
type Type int
|
||||
|
||||
// Possible Type values. Note that the zero value is not valid: callers must
|
||||
// always specify one of Raw or Datagram when calling Listen.
|
||||
const (
|
||||
_ Type = iota
|
||||
Raw
|
||||
Datagram
|
||||
)
|
||||
|
||||
// Listen opens a packet sockets connection on the specified interface, using
|
||||
// the given socket type and protocol values.
|
||||
//
|
||||
// The socket type must be one of the Type constants: Raw or Datagram.
|
||||
//
|
||||
// The Config specifies optional configuration for the Conn. A nil *Config
|
||||
// applies the default configuration.
|
||||
func Listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
|
||||
l, err := listen(ifi, socketType, protocol, cfg)
|
||||
if err != nil {
|
||||
return nil, opError(opListen, err, &Addr{HardwareAddr: ifi.HardwareAddr})
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// TODO(mdlayher): we want to support FileConn for advanced use cases, but this
|
||||
// library would also need a big endian protocol value and an interface index.
|
||||
// For now we won't bother, but reconsider in the future.
|
||||
|
||||
var (
|
||||
_ net.PacketConn = &Conn{}
|
||||
_ syscall.Conn = &Conn{}
|
||||
_ bpf.Setter = &Conn{}
|
||||
)
|
||||
|
||||
// A Conn is an Linux packet sockets (AF_PACKET) implementation of a
|
||||
// net.PacketConn.
|
||||
type Conn struct {
|
||||
c *conn
|
||||
|
||||
// Metadata about the local connection.
|
||||
addr *Addr
|
||||
ifIndex int
|
||||
protocol uint16
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (c *Conn) Close() error {
|
||||
return c.opError(opClose, c.c.Close())
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address. The Addr returned is shared by
|
||||
// all invocations of LocalAddr, so do not modify it.
|
||||
func (c *Conn) LocalAddr() net.Addr { return c.addr }
|
||||
|
||||
// ReadFrom implements the net.PacketConn ReadFrom method.
|
||||
func (c *Conn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
return c.readFrom(b)
|
||||
}
|
||||
|
||||
// WriteTo implements the net.PacketConn WriteTo method.
|
||||
func (c *Conn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
return c.writeTo(b, addr)
|
||||
}
|
||||
|
||||
// SetDeadline implements the net.PacketConn SetDeadline method.
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
return c.opError(opSet, c.c.SetDeadline(t))
|
||||
}
|
||||
|
||||
// SetReadDeadline implements the net.PacketConn SetReadDeadline method.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return c.opError(opSet, c.c.SetReadDeadline(t))
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements the net.PacketConn SetWriteDeadline method.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
return c.opError(opSet, c.c.SetWriteDeadline(t))
|
||||
}
|
||||
|
||||
// SetBPF attaches an assembled BPF program to the Conn.
|
||||
func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
|
||||
return c.opError(opSetsockopt, c.c.SetBPF(filter))
|
||||
}
|
||||
|
||||
// SetPromiscuous enables or disables promiscuous mode on the Conn, allowing it
|
||||
// to receive traffic that is not addressed to the Conn's network interface.
|
||||
func (c *Conn) SetPromiscuous(enable bool) error {
|
||||
return c.setPromiscuous(enable)
|
||||
}
|
||||
|
||||
// Stats contains statistics about a Conn reported by the Linux kernel.
|
||||
type Stats struct {
|
||||
// The total number of packets received.
|
||||
Packets uint32
|
||||
|
||||
// The number of packets dropped.
|
||||
Drops uint32
|
||||
|
||||
// The total number of times that a receive queue is frozen. May be zero if
|
||||
// the Linux kernel is not new enough to support TPACKET_V3 statistics.
|
||||
FreezeQueueCount uint32
|
||||
}
|
||||
|
||||
// Stats retrieves statistics about the Conn from the Linux kernel.
|
||||
//
|
||||
// Note that calling Stats will reset the kernel's internal counters for this
|
||||
// Conn. If you want to maintain cumulative statistics by polling Stats over
|
||||
// time, you must do so in your calling code.
|
||||
func (c *Conn) Stats() (*Stats, error) { return c.stats() }
|
||||
|
||||
// SyscallConn returns a raw network connection. This implements the
|
||||
// syscall.Conn interface.
|
||||
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
|
||||
rc, err := c.c.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, c.opError(opSyscallConn, err)
|
||||
}
|
||||
|
||||
return &rawConn{
|
||||
rc: rc,
|
||||
addr: c.addr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// opError is a convenience for the function opError that also passes the local
|
||||
// and remote addresses of the Conn.
|
||||
func (c *Conn) opError(op string, err error) error {
|
||||
return opError(op, err, c.addr)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): see if we can port smarter net.OpError logic into
|
||||
// socket.Conn's SyscallConn type to avoid the need for this wrapper.
|
||||
|
||||
var _ syscall.RawConn = &rawConn{}
|
||||
|
||||
// A rawConn is a syscall.RawConn that wraps an internal syscall.RawConn in order
|
||||
// to produce net.OpError error values.
|
||||
type rawConn struct {
|
||||
rc syscall.RawConn
|
||||
addr *Addr
|
||||
}
|
||||
|
||||
// Control implements the syscall.RawConn Control method.
|
||||
func (rc *rawConn) Control(fn func(fd uintptr)) error {
|
||||
return rc.opError(opRawControl, rc.rc.Control(fn))
|
||||
}
|
||||
|
||||
// Control implements the syscall.RawConn Read method.
|
||||
func (rc *rawConn) Read(fn func(fd uintptr) (done bool)) error {
|
||||
return rc.opError(opRawRead, rc.rc.Read(fn))
|
||||
}
|
||||
|
||||
// Control implements the syscall.RawConn Write method.
|
||||
func (rc *rawConn) Write(fn func(fd uintptr) (done bool)) error {
|
||||
return rc.opError(opRawWrite, rc.rc.Write(fn))
|
||||
}
|
||||
|
||||
// opError is a convenience for the function opError that also passes the
|
||||
// address of the rawConn.
|
||||
func (rc *rawConn) opError(op string, err error) error {
|
||||
return opError(op, err, rc.addr)
|
||||
}
|
||||
|
||||
var _ net.Addr = &Addr{}
|
||||
|
||||
// TODO(mdlayher): expose sll_hatype and sll_pkttype on receive Addr only.
|
||||
|
||||
// An Addr is a physical-layer address.
|
||||
type Addr struct {
|
||||
HardwareAddr net.HardwareAddr
|
||||
}
|
||||
|
||||
// Network returns the address's network name, "packet".
|
||||
func (a *Addr) Network() string { return network }
|
||||
|
||||
// String returns the string representation of an Addr.
|
||||
func (a *Addr) String() string {
|
||||
return a.HardwareAddr.String()
|
||||
}
|
||||
|
||||
// opError unpacks err if possible, producing a net.OpError with the input
|
||||
// parameters in order to implement net.PacketConn. As a convenience, opError
|
||||
// returns nil if the input error is nil.
|
||||
func opError(op string, err error, local net.Addr) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(mdlayher): try to comply with net.PacketConn as best as we can; land
|
||||
// a nettest.TestPacketConn API upstream.
|
||||
return &net.OpError{
|
||||
Op: op,
|
||||
Net: network,
|
||||
Addr: local,
|
||||
Err: err,
|
||||
}
|
||||
}
|
248
vendor/github.com/mdlayher/packet/packet_linux.go
generated
vendored
Normal file
248
vendor/github.com/mdlayher/packet/packet_linux.go
generated
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/josharian/native"
|
||||
"github.com/mdlayher/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// A conn is the net.PacketConn implementation for packet sockets. We can use
|
||||
// socket.Conn directly on Linux to implement most of the necessary methods.
|
||||
type conn = socket.Conn
|
||||
|
||||
// readFrom implements the net.PacketConn ReadFrom method using recvfrom(2).
|
||||
func (c *Conn) readFrom(b []byte) (int, net.Addr, error) {
|
||||
// From net.PacketConn documentation:
|
||||
//
|
||||
// "[ReadFrom] returns the number of bytes read (0 <= n <= len(p)) and any
|
||||
// error encountered. Callers should always process the n > 0 bytes returned
|
||||
// before considering the error err."
|
||||
//
|
||||
// c.opError will return nil if no error, but either way we return all the
|
||||
// information that we have.
|
||||
n, sa, err := c.c.Recvfrom(context.Background(), b, 0)
|
||||
return n, fromSockaddr(sa), c.opError(opRead, err)
|
||||
}
|
||||
|
||||
// writeTo implements the net.PacketConn WriteTo method.
|
||||
func (c *Conn) writeTo(b []byte, addr net.Addr) (int, error) {
|
||||
sa, err := c.toSockaddr("sendto", addr)
|
||||
if err != nil {
|
||||
return 0, c.opError(opWrite, err)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): it's curious that unix.Sendto does not return the number
|
||||
// of bytes actually sent. Fake it for now, but investigate upstream.
|
||||
if err := c.c.Sendto(context.Background(), b, 0, sa); err != nil {
|
||||
return 0, c.opError(opWrite, err)
|
||||
}
|
||||
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// setPromiscuous wraps setsockopt(2) for the unix.PACKET_MR_PROMISC option.
|
||||
func (c *Conn) setPromiscuous(enable bool) error {
|
||||
mreq := unix.PacketMreq{
|
||||
Ifindex: int32(c.ifIndex),
|
||||
Type: unix.PACKET_MR_PROMISC,
|
||||
}
|
||||
|
||||
membership := unix.PACKET_DROP_MEMBERSHIP
|
||||
if enable {
|
||||
membership = unix.PACKET_ADD_MEMBERSHIP
|
||||
}
|
||||
|
||||
return c.opError(
|
||||
opSetsockopt,
|
||||
c.c.SetsockoptPacketMreq(unix.SOL_PACKET, membership, &mreq),
|
||||
)
|
||||
}
|
||||
|
||||
// stats wraps getsockopt(2) for tpacket_stats* types.
|
||||
func (c *Conn) stats() (*Stats, error) {
|
||||
const (
|
||||
level = unix.SOL_PACKET
|
||||
name = unix.PACKET_STATISTICS
|
||||
)
|
||||
|
||||
// Try to fetch V3 statistics first, they contain more detailed information.
|
||||
if stats, err := c.c.GetsockoptTpacketStatsV3(level, name); err == nil {
|
||||
return &Stats{
|
||||
Packets: stats.Packets,
|
||||
Drops: stats.Drops,
|
||||
FreezeQueueCount: stats.Freeze_q_cnt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// There was an error fetching v3 stats, try to fall back.
|
||||
stats, err := c.c.GetsockoptTpacketStats(level, name)
|
||||
if err != nil {
|
||||
return nil, c.opError(opGetsockopt, err)
|
||||
}
|
||||
|
||||
return &Stats{
|
||||
Packets: stats.Packets,
|
||||
Drops: stats.Drops,
|
||||
// FreezeQueueCount is not present.
|
||||
}, nil
|
||||
}
|
||||
|
||||
// listen is the entry point for Listen on Linux.
|
||||
func listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
|
||||
if cfg == nil {
|
||||
// Default configuration.
|
||||
cfg = &Config{}
|
||||
}
|
||||
|
||||
// Convert Type to the matching SOCK_* constant.
|
||||
var typ int
|
||||
switch socketType {
|
||||
case Raw:
|
||||
typ = unix.SOCK_RAW
|
||||
case Datagram:
|
||||
typ = unix.SOCK_DGRAM
|
||||
default:
|
||||
return nil, errors.New("packet: invalid Type value")
|
||||
}
|
||||
|
||||
// Protocol is intentionally zero in call to socket(2); we can set it on
|
||||
// bind(2) instead. Package raw notes: "Do not specify a protocol to avoid
|
||||
// capturing packets which to not match cfg.Filter."
|
||||
c, err := socket.Socket(unix.AF_PACKET, typ, 0, network, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := bind(c, ifi.Index, protocol, cfg)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// bind binds the *socket.Conn to finalize *Conn setup.
|
||||
func bind(c *socket.Conn, ifIndex, protocol int, cfg *Config) (*Conn, error) {
|
||||
if len(cfg.Filter) > 0 {
|
||||
// The caller wants to apply a BPF filter before bind(2).
|
||||
if err := c.SetBPF(cfg.Filter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// packet(7) says we sll_protocol must be in network byte order.
|
||||
pnet, err := htons(protocol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(mdlayher): investigate the possibility of sll_ifindex = 0 because we
|
||||
// could bind to any interface.
|
||||
err = c.Bind(&unix.SockaddrLinklayer{
|
||||
Protocol: pnet,
|
||||
Ifindex: ifIndex,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lsa, err := c.Getsockname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the physical layer address; sll_halen tells us how many bytes of
|
||||
// sll_addr we should treat as valid.
|
||||
lsall := lsa.(*unix.SockaddrLinklayer)
|
||||
addr := make(net.HardwareAddr, lsall.Halen)
|
||||
copy(addr, lsall.Addr[:])
|
||||
|
||||
return &Conn{
|
||||
c: c,
|
||||
|
||||
addr: &Addr{HardwareAddr: addr},
|
||||
ifIndex: ifIndex,
|
||||
protocol: pnet,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// fromSockaddr converts an opaque unix.Sockaddr to *Addr. If sa is nil, it
|
||||
// returns nil. It panics if sa is not of type *unix.SockaddrLinklayer.
|
||||
func fromSockaddr(sa unix.Sockaddr) *Addr {
|
||||
if sa == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sall := sa.(*unix.SockaddrLinklayer)
|
||||
|
||||
return &Addr{
|
||||
// The syscall already allocated sa; just slice into it with the
|
||||
// appropriate length and type conversion rather than making a copy.
|
||||
HardwareAddr: net.HardwareAddr(sall.Addr[:sall.Halen]),
|
||||
}
|
||||
}
|
||||
|
||||
// toSockaddr converts a net.Addr to an opaque unix.Sockaddr. It returns an
|
||||
// error if the fields cannot be packed into a *unix.SockaddrLinklayer.
|
||||
func (c *Conn) toSockaddr(
|
||||
op string,
|
||||
addr net.Addr,
|
||||
) (unix.Sockaddr, error) {
|
||||
// The typical error convention for net.Conn types is
|
||||
// net.OpError(os.SyscallError(syscall.Errno)), so all calls here should
|
||||
// return os.SyscallError(syscall.Errno) so the caller can apply the final
|
||||
// net.OpError wrapper.
|
||||
|
||||
// Ensure the correct Addr type.
|
||||
a, ok := addr.(*Addr)
|
||||
if !ok || a.HardwareAddr == nil {
|
||||
return nil, os.NewSyscallError(op, unix.EINVAL)
|
||||
}
|
||||
|
||||
// Pack Addr and Conn metadata into the appropriate sockaddr fields. From
|
||||
// packet(7):
|
||||
//
|
||||
// "When you send packets it is enough to specify sll_family, sll_addr,
|
||||
// sll_halen, sll_ifindex, and sll_protocol. The other fields should be 0."
|
||||
//
|
||||
// sll_family is set on the conversion to unix.RawSockaddrLinklayer.
|
||||
sa := unix.SockaddrLinklayer{
|
||||
Ifindex: c.ifIndex,
|
||||
Protocol: c.protocol,
|
||||
}
|
||||
|
||||
// Ensure the input address does not exceed the amount of space available;
|
||||
// for example an IPoIB address is 20 bytes.
|
||||
if len(a.HardwareAddr) > len(sa.Addr) {
|
||||
return nil, os.NewSyscallError(op, unix.EINVAL)
|
||||
}
|
||||
|
||||
sa.Halen = uint8(len(a.HardwareAddr))
|
||||
copy(sa.Addr[:], a.HardwareAddr)
|
||||
|
||||
return &sa, nil
|
||||
}
|
||||
|
||||
// htons converts a short (uint16) from host-to-network byte order.
|
||||
func htons(i int) (uint16, error) {
|
||||
if i < 0 || i > math.MaxUint16 {
|
||||
return 0, errors.New("packet: protocol value out of range")
|
||||
}
|
||||
|
||||
// Store as big endian, retrieve as native endian.
|
||||
var b [2]byte
|
||||
binary.BigEndian.PutUint16(b[:], uint16(i))
|
||||
|
||||
return native.Endian.Uint16(b[:]), nil
|
||||
}
|
33
vendor/github.com/mdlayher/packet/packet_others.go
generated
vendored
Normal file
33
vendor/github.com/mdlayher/packet/packet_others.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
// errUnimplemented is returned by all functions on non-Linux platforms.
|
||||
var errUnimplemented = fmt.Errorf("packet: not implemented on %s", runtime.GOOS)
|
||||
|
||||
func listen(_ *net.Interface, _ Type, _ int, _ *Config) (*Conn, error) { return nil, errUnimplemented }
|
||||
|
||||
func (*Conn) readFrom(_ []byte) (int, net.Addr, error) { return 0, nil, errUnimplemented }
|
||||
func (*Conn) writeTo(_ []byte, _ net.Addr) (int, error) { return 0, errUnimplemented }
|
||||
func (*Conn) setPromiscuous(_ bool) error { return errUnimplemented }
|
||||
func (*Conn) stats() (*Stats, error) { return nil, errUnimplemented }
|
||||
|
||||
type conn struct{}
|
||||
|
||||
func (*conn) Close() error { return errUnimplemented }
|
||||
func (*conn) SetDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetReadDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetWriteDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetBPF(_ []bpf.RawInstruction) error { return errUnimplemented }
|
||||
func (*conn) SyscallConn() (syscall.RawConn, error) { return nil, errUnimplemented }
|
94
vendor/github.com/mdlayher/socket/CHANGELOG.md
generated
vendored
Normal file
94
vendor/github.com/mdlayher/socket/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.5.1
|
||||
|
||||
- [Improvement]: revert `go.mod` to Go 1.20 to [resolve an issue around Go
|
||||
module version upgrades](https://github.com/mdlayher/socket/issues/13).
|
||||
|
||||
## v0.5.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.21+.
|
||||
Users on older versions of Go must use v0.4.1.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go.
|
||||
- [New API]: add `socket.Conn` wrappers for various `Getsockopt` and
|
||||
`Setsockopt` system calls.
|
||||
|
||||
## v0.4.1
|
||||
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/2a14ceef4da279de1f957c5761fffcc6c87bbd3b):
|
||||
ensure `socket.Conn` can be used with non-socket file descriptors by handling
|
||||
`ENOTSOCK` in the constructor.
|
||||
|
||||
## v0.4.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.18+.
|
||||
Users on older versions of Go must use v0.3.0.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go so we can begin using
|
||||
modern versions of `x/sys` and other dependencies.
|
||||
|
||||
## v0.3.0
|
||||
|
||||
**This is the last release of package socket that supports Go 1.17 and below.**
|
||||
|
||||
- [New API/API change] [PR](https://github.com/mdlayher/socket/pull/8):
|
||||
numerous `socket.Conn` methods now support context cancelation. Future
|
||||
releases will continue adding support as needed.
|
||||
- New `ReadContext` and `WriteContext` methods.
|
||||
- `Connect`, `Recvfrom`, `Recvmsg`, `Sendmsg`, and `Sendto` methods now accept
|
||||
a context.
|
||||
- `Sendto` parameter order was also fixed to match the underlying syscall.
|
||||
|
||||
## v0.2.3
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/a425d96e0f772c053164f8ce4c9c825380a98086):
|
||||
`socket.Conn` has new `Pidfd*` methods for wrapping the `pidfd_*(2)` family of
|
||||
system calls.
|
||||
|
||||
## v0.2.2
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/a2429f1dfe8ec2586df5a09f50ead865276cd027):
|
||||
`socket.Conn` has new `IoctlKCM*` methods for wrapping `ioctl(2)` for `AF_KCM`
|
||||
operations.
|
||||
|
||||
## v0.2.1
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/b18ddbe9caa0e34552b4409a3aa311cb460d2f99):
|
||||
`socket.Conn` has a new `SetsockoptPacketMreq` method for wrapping
|
||||
`setsockopt(2)` for `AF_PACKET` socket options.
|
||||
|
||||
## v0.2.0
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/6e912a68523c45e5fd899239f4b46c402dd856da):
|
||||
`socket.FileConn` can be used to create a `socket.Conn` from an existing
|
||||
`os.File`, which may be provided by systemd socket activation or another
|
||||
external mechanism.
|
||||
- [API change] [commit](https://github.com/mdlayher/socket/commit/66d61f565188c23fe02b24099ddc856d538bf1a7):
|
||||
`socket.Conn.Connect` now returns the `unix.Sockaddr` value provided by
|
||||
`getpeername(2)`, since we have to invoke that system call anyway to verify
|
||||
that a connection to a remote peer was successfully established.
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/b60b2dbe0ac3caff2338446a150083bde8c5c19c):
|
||||
check the correct error from `unix.GetsockoptInt` in the `socket.Conn.Connect`
|
||||
method. Thanks @vcabbage!
|
||||
|
||||
## v0.1.2
|
||||
|
||||
- [Bug Fix]: `socket.Conn.Connect` now properly checks the `SO_ERROR` socket
|
||||
option value after calling `connect(2)` to verify whether or not a connection
|
||||
could successfully be established. This means that `Connect` should now report
|
||||
an error for an `AF_INET` TCP connection refused or `AF_VSOCK` connection
|
||||
reset by peer.
|
||||
- [New API]: add `socket.Conn.Getpeername` for use in `Connect`, but also for
|
||||
use by external callers.
|
||||
|
||||
## v0.1.1
|
||||
|
||||
- [New API]: `socket.Conn` now has `CloseRead`, `CloseWrite`, and `Shutdown`
|
||||
methods.
|
||||
- [Improvement]: internal rework to more robustly handle various errors.
|
||||
|
||||
## v0.1.0
|
||||
|
||||
- Initial unstable release. Most functionality has been developed and ported
|
||||
from package [`netlink`](https://github.com/mdlayher/netlink).
|
9
vendor/github.com/mdlayher/socket/LICENSE.md
generated
vendored
Normal file
9
vendor/github.com/mdlayher/socket/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (C) 2021 Matt Layher
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
vendor/github.com/mdlayher/socket/README.md
generated
vendored
Normal file
23
vendor/github.com/mdlayher/socket/README.md
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# socket [](https://github.com/mdlayher/socket/actions) [](https://pkg.go.dev/github.com/mdlayher/socket) [](https://goreportcard.com/report/github.com/mdlayher/socket)
|
||||
|
||||
Package `socket` provides a low-level network connection type which integrates
|
||||
with Go's runtime network poller to provide asynchronous I/O and deadline
|
||||
support. MIT Licensed.
|
||||
|
||||
This package focuses on UNIX-like operating systems which make use of BSD
|
||||
sockets system call APIs. It is meant to be used as a foundation for the
|
||||
creation of operating system-specific socket packages, for socket families such
|
||||
as Linux's `AF_NETLINK`, `AF_PACKET`, or `AF_VSOCK`. This package should not be
|
||||
used directly in end user applications.
|
||||
|
||||
Any use of package socket should be guarded by build tags, as one would also
|
||||
use when importing the `syscall` or `golang.org/x/sys` packages.
|
||||
|
||||
## Stability
|
||||
|
||||
See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
|
||||
releases.
|
||||
|
||||
This package only supports the two most recent major versions of Go, mirroring
|
||||
Go's own release policy. Older versions of Go may lack critical features and bug
|
||||
fixes which are necessary for this package to function correctly.
|
23
vendor/github.com/mdlayher/socket/accept.go
generated
vendored
Normal file
23
vendor/github.com/mdlayher/socket/accept.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
//go:build !dragonfly && !freebsd && !illumos && !linux
|
||||
// +build !dragonfly,!freebsd,!illumos,!linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const sysAccept = "accept"
|
||||
|
||||
// accept wraps accept(2).
|
||||
func accept(fd, flags int) (int, unix.Sockaddr, error) {
|
||||
if flags != 0 {
|
||||
// These operating systems have no support for flags to accept(2).
|
||||
return 0, nil, fmt.Errorf("socket: Conn.Accept flags are ineffective on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
return unix.Accept(fd)
|
||||
}
|
15
vendor/github.com/mdlayher/socket/accept4.go
generated
vendored
Normal file
15
vendor/github.com/mdlayher/socket/accept4.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build dragonfly || freebsd || illumos || linux
|
||||
// +build dragonfly freebsd illumos linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const sysAccept = "accept4"
|
||||
|
||||
// accept wraps accept4(2).
|
||||
func accept(fd, flags int) (int, unix.Sockaddr, error) {
|
||||
return unix.Accept4(fd, flags)
|
||||
}
|
894
vendor/github.com/mdlayher/socket/conn.go
generated
vendored
Normal file
894
vendor/github.com/mdlayher/socket/conn.go
generated
vendored
Normal file
@ -0,0 +1,894 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock in an expected public interface for convenience.
|
||||
var _ interface {
|
||||
io.ReadWriteCloser
|
||||
syscall.Conn
|
||||
SetDeadline(t time.Time) error
|
||||
SetReadDeadline(t time.Time) error
|
||||
SetWriteDeadline(t time.Time) error
|
||||
} = &Conn{}
|
||||
|
||||
// A Conn is a low-level network connection which integrates with Go's runtime
|
||||
// network poller to provide asynchronous I/O and deadline support.
|
||||
//
|
||||
// Many of a Conn's blocking methods support net.Conn deadlines as well as
|
||||
// cancelation via context. Note that passing a context with a deadline set will
|
||||
// override any of the previous deadlines set by calls to the SetDeadline family
|
||||
// of methods.
|
||||
type Conn struct {
|
||||
// Indicates whether or not Conn.Close has been called. Must be accessed
|
||||
// atomically. Atomics definitions must come first in the Conn struct.
|
||||
closed uint32
|
||||
|
||||
// A unique name for the Conn which is also associated with derived file
|
||||
// descriptors such as those created by accept(2).
|
||||
name string
|
||||
|
||||
// facts contains information we have determined about Conn to trigger
|
||||
// alternate behavior in certain functions.
|
||||
facts facts
|
||||
|
||||
// Provides access to the underlying file registered with the runtime
|
||||
// network poller, and arbitrary raw I/O calls.
|
||||
fd *os.File
|
||||
rc syscall.RawConn
|
||||
}
|
||||
|
||||
// facts contains facts about a Conn.
|
||||
type facts struct {
|
||||
// isStream reports whether this is a streaming descriptor, as opposed to a
|
||||
// packet-based descriptor like a UDP socket.
|
||||
isStream bool
|
||||
|
||||
// zeroReadIsEOF reports Whether a zero byte read indicates EOF. This is
|
||||
// false for a message based socket connection.
|
||||
zeroReadIsEOF bool
|
||||
}
|
||||
|
||||
// A Config contains options for a Conn.
|
||||
type Config struct {
|
||||
// NetNS specifies the Linux network namespace the Conn will operate in.
|
||||
// This option is unsupported on other operating systems.
|
||||
//
|
||||
// If set (non-zero), Conn will enter the specified network namespace and an
|
||||
// error will occur in Socket if the operation fails.
|
||||
//
|
||||
// If not set (zero), a best-effort attempt will be made to enter the
|
||||
// network namespace of the calling thread: this means that any changes made
|
||||
// to the calling thread's network namespace will also be reflected in Conn.
|
||||
// If this operation fails (due to lack of permissions or because network
|
||||
// namespaces are disabled by kernel configuration), Socket will not return
|
||||
// an error, and the Conn will operate in the default network namespace of
|
||||
// the process. This enables non-privileged use of Conn in applications
|
||||
// which do not require elevated privileges.
|
||||
//
|
||||
// Entering a network namespace is a privileged operation (root or
|
||||
// CAP_SYS_ADMIN are required), and most applications should leave this set
|
||||
// to 0.
|
||||
NetNS int
|
||||
}
|
||||
|
||||
// High-level methods which provide convenience over raw system calls.
|
||||
|
||||
// Close closes the underlying file descriptor for the Conn, which also causes
|
||||
// all in-flight I/O operations to immediately unblock and return errors. Any
|
||||
// subsequent uses of Conn will result in EBADF.
|
||||
func (c *Conn) Close() error {
|
||||
// The caller has expressed an intent to close the socket, so immediately
|
||||
// increment s.closed to force further calls to result in EBADF before also
|
||||
// closing the file descriptor to unblock any outstanding operations.
|
||||
//
|
||||
// Because other operations simply check for s.closed != 0, we will permit
|
||||
// double Close, which would increment s.closed beyond 1.
|
||||
if atomic.AddUint32(&c.closed, 1) != 1 {
|
||||
// Multiple Close calls.
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.NewSyscallError("close", c.fd.Close())
|
||||
}
|
||||
|
||||
// CloseRead shuts down the reading side of the Conn. Most callers should just
|
||||
// use Close.
|
||||
func (c *Conn) CloseRead() error { return c.Shutdown(unix.SHUT_RD) }
|
||||
|
||||
// CloseWrite shuts down the writing side of the Conn. Most callers should just
|
||||
// use Close.
|
||||
func (c *Conn) CloseWrite() error { return c.Shutdown(unix.SHUT_WR) }
|
||||
|
||||
// Read reads directly from the underlying file descriptor.
|
||||
func (c *Conn) Read(b []byte) (int, error) { return c.fd.Read(b) }
|
||||
|
||||
// ReadContext reads from the underlying file descriptor with added support for
|
||||
// context cancelation.
|
||||
func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) {
|
||||
if c.facts.isStream && len(b) > maxRW {
|
||||
b = b[:maxRW]
|
||||
}
|
||||
|
||||
n, err := readT(c, ctx, "read", func(fd int) (int, error) {
|
||||
return unix.Read(fd, b)
|
||||
})
|
||||
if n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return n, os.NewSyscallError("read", err)
|
||||
}
|
||||
|
||||
// Write writes directly to the underlying file descriptor.
|
||||
func (c *Conn) Write(b []byte) (int, error) { return c.fd.Write(b) }
|
||||
|
||||
// WriteContext writes to the underlying file descriptor with added support for
|
||||
// context cancelation.
|
||||
func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) {
|
||||
var (
|
||||
n, nn int
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, "write", func(fd int) error {
|
||||
max := len(b)
|
||||
if c.facts.isStream && max-nn > maxRW {
|
||||
max = nn + maxRW
|
||||
}
|
||||
|
||||
n, err = unix.Write(fd, b[nn:max])
|
||||
if n > 0 {
|
||||
nn += n
|
||||
}
|
||||
if nn == len(b) {
|
||||
return err
|
||||
}
|
||||
if n == 0 && err == nil {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
if doErr != nil {
|
||||
return 0, doErr
|
||||
}
|
||||
|
||||
return nn, os.NewSyscallError("write", err)
|
||||
}
|
||||
|
||||
// SetDeadline sets both the read and write deadlines associated with the Conn.
|
||||
func (c *Conn) SetDeadline(t time.Time) error { return c.fd.SetDeadline(t) }
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the Conn.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error { return c.fd.SetReadDeadline(t) }
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the Conn.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error { return c.fd.SetWriteDeadline(t) }
|
||||
|
||||
// ReadBuffer gets the size of the operating system's receive buffer associated
|
||||
// with the Conn.
|
||||
func (c *Conn) ReadBuffer() (int, error) {
|
||||
return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF)
|
||||
}
|
||||
|
||||
// WriteBuffer gets the size of the operating system's transmit buffer
|
||||
// associated with the Conn.
|
||||
func (c *Conn) WriteBuffer() (int, error) {
|
||||
return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF)
|
||||
}
|
||||
|
||||
// SetReadBuffer sets the size of the operating system's receive buffer
|
||||
// associated with the Conn.
|
||||
//
|
||||
// When called with elevated privileges on Linux, the SO_RCVBUFFORCE option will
|
||||
// be used to override operating system limits. Otherwise SO_RCVBUF is used
|
||||
// (which obeys operating system limits).
|
||||
func (c *Conn) SetReadBuffer(bytes int) error { return c.setReadBuffer(bytes) }
|
||||
|
||||
// SetWriteBuffer sets the size of the operating system's transmit buffer
|
||||
// associated with the Conn.
|
||||
//
|
||||
// When called with elevated privileges on Linux, the SO_SNDBUFFORCE option will
|
||||
// be used to override operating system limits. Otherwise SO_SNDBUF is used
|
||||
// (which obeys operating system limits).
|
||||
func (c *Conn) SetWriteBuffer(bytes int) error { return c.setWriteBuffer(bytes) }
|
||||
|
||||
// SyscallConn returns a raw network connection. This implements the
|
||||
// syscall.Conn interface.
|
||||
//
|
||||
// SyscallConn is intended for advanced use cases, such as getting and setting
|
||||
// arbitrary socket options using the socket's file descriptor. If possible,
|
||||
// those operations should be performed using methods on Conn instead.
|
||||
//
|
||||
// Once invoked, it is the caller's responsibility to ensure that operations
|
||||
// performed using Conn and the syscall.RawConn do not conflict with each other.
|
||||
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return nil, os.NewSyscallError("syscallconn", unix.EBADF)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): mutex or similar to enforce syscall.RawConn contract of
|
||||
// FD remaining valid for duration of calls?
|
||||
return c.rc, nil
|
||||
}
|
||||
|
||||
// Socket wraps the socket(2) system call to produce a Conn. domain, typ, and
|
||||
// proto are passed directly to socket(2), and name should be a unique name for
|
||||
// the socket type such as "netlink" or "vsock".
|
||||
//
|
||||
// The cfg parameter specifies optional configuration for the Conn. If nil, no
|
||||
// additional configuration will be applied.
|
||||
//
|
||||
// If the operating system supports SOCK_CLOEXEC and SOCK_NONBLOCK, they are
|
||||
// automatically applied to typ to mirror the standard library's socket flag
|
||||
// behaviors.
|
||||
func Socket(domain, typ, proto int, name string, cfg *Config) (*Conn, error) {
|
||||
if cfg == nil {
|
||||
cfg = &Config{}
|
||||
}
|
||||
|
||||
if cfg.NetNS == 0 {
|
||||
// Non-Linux or no network namespace.
|
||||
return socket(domain, typ, proto, name)
|
||||
}
|
||||
|
||||
// Linux only: create Conn in the specified network namespace.
|
||||
return withNetNS(cfg.NetNS, func() (*Conn, error) {
|
||||
return socket(domain, typ, proto, name)
|
||||
})
|
||||
}
|
||||
|
||||
// socket is the internal, cross-platform entry point for socket(2).
|
||||
func socket(domain, typ, proto int, name string) (*Conn, error) {
|
||||
var (
|
||||
fd int
|
||||
err error
|
||||
)
|
||||
|
||||
for {
|
||||
fd, err = unix.Socket(domain, typ|socketFlags, proto)
|
||||
switch {
|
||||
case err == nil:
|
||||
// Some OSes already set CLOEXEC with typ.
|
||||
if !flagCLOEXEC {
|
||||
unix.CloseOnExec(fd)
|
||||
}
|
||||
|
||||
// No error, prepare the Conn.
|
||||
return New(fd, name)
|
||||
case !ready(err):
|
||||
// System call interrupted or not ready, try again.
|
||||
continue
|
||||
case err == unix.EINVAL, err == unix.EPROTONOSUPPORT:
|
||||
// On Linux, SOCK_NONBLOCK and SOCK_CLOEXEC were introduced in
|
||||
// 2.6.27. On FreeBSD, both flags were introduced in FreeBSD 10.
|
||||
// EINVAL and EPROTONOSUPPORT check for earlier versions of these
|
||||
// OSes respectively.
|
||||
//
|
||||
// Mirror what the standard library does when creating file
|
||||
// descriptors: avoid racing a fork/exec with the creation of new
|
||||
// file descriptors, so that child processes do not inherit socket
|
||||
// file descriptors unexpectedly.
|
||||
//
|
||||
// For a more thorough explanation, see similar work in the Go tree:
|
||||
// func sysSocket in net/sock_cloexec.go, as well as the detailed
|
||||
// comment in syscall/exec_unix.go.
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err = unix.Socket(domain, typ, proto)
|
||||
if err != nil {
|
||||
syscall.ForkLock.RUnlock()
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
unix.CloseOnExec(fd)
|
||||
syscall.ForkLock.RUnlock()
|
||||
|
||||
return New(fd, name)
|
||||
default:
|
||||
// Unhandled error.
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FileConn returns a copy of the network connection corresponding to the open
|
||||
// file. It is the caller's responsibility to close the file when finished.
|
||||
// Closing the Conn does not affect the File, and closing the File does not
|
||||
// affect the Conn.
|
||||
func FileConn(f *os.File, name string) (*Conn, error) {
|
||||
// First we'll try to do fctnl(2) with F_DUPFD_CLOEXEC because we can dup
|
||||
// the file descriptor and set the flag in one syscall.
|
||||
fd, err := unix.FcntlInt(f.Fd(), unix.F_DUPFD_CLOEXEC, 0)
|
||||
switch err {
|
||||
case nil:
|
||||
// OK, ready to set up non-blocking I/O.
|
||||
return New(fd, name)
|
||||
case unix.EINVAL:
|
||||
// The kernel rejected our fcntl(2), fall back to separate dup(2) and
|
||||
// setting close on exec.
|
||||
//
|
||||
// Mirror what the standard library does when creating file descriptors:
|
||||
// avoid racing a fork/exec with the creation of new file descriptors,
|
||||
// so that child processes do not inherit socket file descriptors
|
||||
// unexpectedly.
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err := unix.Dup(fd)
|
||||
if err != nil {
|
||||
syscall.ForkLock.RUnlock()
|
||||
return nil, os.NewSyscallError("dup", err)
|
||||
}
|
||||
unix.CloseOnExec(fd)
|
||||
syscall.ForkLock.RUnlock()
|
||||
|
||||
return New(fd, name)
|
||||
default:
|
||||
// Any other errors.
|
||||
return nil, os.NewSyscallError("fcntl", err)
|
||||
}
|
||||
}
|
||||
|
||||
// New wraps an existing file descriptor to create a Conn. name should be a
|
||||
// unique name for the socket type such as "netlink" or "vsock".
|
||||
//
|
||||
// Most callers should use Socket or FileConn to construct a Conn. New is
|
||||
// intended for integrating with specific system calls which provide a file
|
||||
// descriptor that supports asynchronous I/O. The file descriptor is immediately
|
||||
// set to nonblocking mode and registered with Go's runtime network poller for
|
||||
// future I/O operations.
|
||||
//
|
||||
// Unlike FileConn, New does not duplicate the existing file descriptor in any
|
||||
// way. The returned Conn takes ownership of the underlying file descriptor.
|
||||
func New(fd int, name string) (*Conn, error) {
|
||||
// All Conn I/O is nonblocking for integration with Go's runtime network
|
||||
// poller. Depending on the OS this might already be set but it can't hurt
|
||||
// to set it again.
|
||||
if err := unix.SetNonblock(fd, true); err != nil {
|
||||
return nil, os.NewSyscallError("setnonblock", err)
|
||||
}
|
||||
|
||||
// os.NewFile registers the non-blocking file descriptor with the runtime
|
||||
// poller, which is then used for most subsequent operations except those
|
||||
// that require raw I/O via SyscallConn.
|
||||
//
|
||||
// See also: https://golang.org/pkg/os/#NewFile
|
||||
f := os.NewFile(uintptr(fd), name)
|
||||
rc, err := f.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
name: name,
|
||||
fd: f,
|
||||
rc: rc,
|
||||
}
|
||||
|
||||
// Probe the file descriptor for socket settings.
|
||||
sotype, err := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_TYPE)
|
||||
switch {
|
||||
case err == nil:
|
||||
// File is a socket, check its properties.
|
||||
c.facts = facts{
|
||||
isStream: sotype == unix.SOCK_STREAM,
|
||||
zeroReadIsEOF: sotype != unix.SOCK_DGRAM && sotype != unix.SOCK_RAW,
|
||||
}
|
||||
case errors.Is(err, unix.ENOTSOCK):
|
||||
// File is not a socket, treat it as a regular file.
|
||||
c.facts = facts{
|
||||
isStream: true,
|
||||
zeroReadIsEOF: true,
|
||||
}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Low-level methods which provide raw system call access.
|
||||
|
||||
// Accept wraps accept(2) or accept4(2) depending on the operating system, but
|
||||
// returns a Conn for the accepted connection rather than a raw file descriptor.
|
||||
//
|
||||
// If the operating system supports accept4(2) (which allows flags),
|
||||
// SOCK_CLOEXEC and SOCK_NONBLOCK are automatically applied to flags to mirror
|
||||
// the standard library's socket flag behaviors.
|
||||
//
|
||||
// If the operating system only supports accept(2) (which does not allow flags)
|
||||
// and flags is not zero, an error will be returned.
|
||||
//
|
||||
// Accept obeys context cancelation and uses the deadline set on the context to
|
||||
// cancel accepting the next connection. If a deadline is set on ctx, this
|
||||
// deadline will override any previous deadlines set using SetDeadline or
|
||||
// SetReadDeadline. Upon return, the read deadline is cleared.
|
||||
func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
nfd int
|
||||
sa unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) {
|
||||
// Either accept(2) or accept4(2) depending on the OS.
|
||||
nfd, sa, err := accept(fd, flags|socketFlags)
|
||||
return ret{nfd, sa}, err
|
||||
})
|
||||
if err != nil {
|
||||
// internal/poll, context error, or user function error.
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Successfully accepted a connection, wrap it in a Conn for use by the
|
||||
// caller.
|
||||
ac, err := New(r.nfd, c.name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ac, r.sa, nil
|
||||
}
|
||||
|
||||
// Bind wraps bind(2).
|
||||
func (c *Conn) Bind(sa unix.Sockaddr) error {
|
||||
return c.control("bind", func(fd int) error { return unix.Bind(fd, sa) })
|
||||
}
|
||||
|
||||
// Connect wraps connect(2). In order to verify that the underlying socket is
|
||||
// connected to a remote peer, Connect calls getpeername(2) and returns the
|
||||
// unix.Sockaddr from that call.
|
||||
//
|
||||
// Connect obeys context cancelation and uses the deadline set on the context to
|
||||
// cancel connecting to a remote peer. If a deadline is set on ctx, this
|
||||
// deadline will override any previous deadlines set using SetDeadline or
|
||||
// SetWriteDeadline. Upon return, the write deadline is cleared.
|
||||
func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, error) {
|
||||
const op = "connect"
|
||||
|
||||
// TODO(mdlayher): it would seem that trying to connect to unbound vsock
|
||||
// listeners by calling Connect multiple times results in ECONNRESET for the
|
||||
// first and nil error for subsequent calls. Do we need to memoize the
|
||||
// error? Check what the stdlib behavior is.
|
||||
|
||||
var (
|
||||
// Track progress between invocations of the write closure. We don't
|
||||
// have an explicit WaitWrite call like internal/poll does, so we have
|
||||
// to wait until the runtime calls the closure again to indicate we can
|
||||
// write.
|
||||
progress uint32
|
||||
|
||||
// Capture closure sockaddr and error.
|
||||
rsa unix.Sockaddr
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, op, func(fd int) error {
|
||||
if atomic.AddUint32(&progress, 1) == 1 {
|
||||
// First call: initiate connect.
|
||||
return unix.Connect(fd, sa)
|
||||
}
|
||||
|
||||
// Subsequent calls: the runtime network poller indicates fd is
|
||||
// writable. Check for errno.
|
||||
errno, gerr := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_ERROR)
|
||||
if gerr != nil {
|
||||
return gerr
|
||||
}
|
||||
if errno != 0 {
|
||||
// Connection is still not ready or failed. If errno indicates
|
||||
// the socket is not ready, we will wait for the next write
|
||||
// event. Otherwise we propagate this errno back to the as a
|
||||
// permanent error.
|
||||
uerr := unix.Errno(errno)
|
||||
err = uerr
|
||||
return uerr
|
||||
}
|
||||
|
||||
// According to internal/poll, it's possible for the runtime network
|
||||
// poller to spuriously wake us and return errno 0 for SO_ERROR.
|
||||
// Make sure we are actually connected to a peer.
|
||||
peer, err := c.Getpeername()
|
||||
if err != nil {
|
||||
// internal/poll unconditionally goes back to WaitWrite.
|
||||
// Synthesize an error that will do the same for us.
|
||||
return unix.EAGAIN
|
||||
}
|
||||
|
||||
// Connection complete.
|
||||
rsa = peer
|
||||
return nil
|
||||
})
|
||||
if doErr != nil {
|
||||
// internal/poll or context error.
|
||||
return nil, doErr
|
||||
}
|
||||
|
||||
if err == unix.EISCONN {
|
||||
// TODO(mdlayher): is this block obsolete with the addition of the
|
||||
// getsockopt SO_ERROR check above?
|
||||
//
|
||||
// EISCONN is reported if the socket is already established and should
|
||||
// not be treated as an error.
|
||||
// - Darwin reports this for at least TCP sockets
|
||||
// - Linux reports this for at least AF_VSOCK sockets
|
||||
return rsa, nil
|
||||
}
|
||||
|
||||
return rsa, os.NewSyscallError(op, err)
|
||||
}
|
||||
|
||||
// Getsockname wraps getsockname(2).
|
||||
func (c *Conn) Getsockname() (unix.Sockaddr, error) {
|
||||
return controlT(c, "getsockname", unix.Getsockname)
|
||||
}
|
||||
|
||||
// Getpeername wraps getpeername(2).
|
||||
func (c *Conn) Getpeername() (unix.Sockaddr, error) {
|
||||
return controlT(c, "getpeername", unix.Getpeername)
|
||||
}
|
||||
|
||||
// GetsockoptICMPv6Filter wraps getsockopt(2) for *unix.ICMPv6Filter values.
|
||||
func (c *Conn) GetsockoptICMPv6Filter(level, opt int) (*unix.ICMPv6Filter, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (*unix.ICMPv6Filter, error) {
|
||||
return unix.GetsockoptICMPv6Filter(fd, level, opt)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptInt wraps getsockopt(2) for integer values.
|
||||
func (c *Conn) GetsockoptInt(level, opt int) (int, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (int, error) {
|
||||
return unix.GetsockoptInt(fd, level, opt)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptString wraps getsockopt(2) for string values.
|
||||
func (c *Conn) GetsockoptString(level, opt int) (string, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (string, error) {
|
||||
return unix.GetsockoptString(fd, level, opt)
|
||||
})
|
||||
}
|
||||
|
||||
// Listen wraps listen(2).
|
||||
func (c *Conn) Listen(n int) error {
|
||||
return c.control("listen", func(fd int) error { return unix.Listen(fd, n) })
|
||||
}
|
||||
|
||||
// Recvmsg wraps recvmsg(2).
|
||||
func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int, int, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
n, oobn, recvflags int
|
||||
from unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) {
|
||||
n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags)
|
||||
return ret{n, oobn, recvflags, from}, err
|
||||
})
|
||||
if r.n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, 0, 0, nil, io.EOF
|
||||
}
|
||||
|
||||
return r.n, r.oobn, r.recvflags, r.from, err
|
||||
}
|
||||
|
||||
// Recvfrom wraps recvfrom(2).
|
||||
func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
n int
|
||||
addr unix.Sockaddr
|
||||
}
|
||||
|
||||
out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) {
|
||||
n, addr, err := unix.Recvfrom(fd, p, flags)
|
||||
return ret{n, addr}, err
|
||||
})
|
||||
if out.n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
return out.n, out.addr, err
|
||||
}
|
||||
|
||||
// Sendmsg wraps sendmsg(2).
|
||||
func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) {
|
||||
return writeT(c, ctx, "sendmsg", func(fd int) (int, error) {
|
||||
return unix.SendmsgN(fd, p, oob, to, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// Sendto wraps sendto(2).
|
||||
func (c *Conn) Sendto(ctx context.Context, p []byte, flags int, to unix.Sockaddr) error {
|
||||
return c.write(ctx, "sendto", func(fd int) error {
|
||||
return unix.Sendto(fd, p, flags, to)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptICMPv6Filter wraps setsockopt(2) for *unix.ICMPv6Filter values.
|
||||
func (c *Conn) SetsockoptICMPv6Filter(level, opt int, filter *unix.ICMPv6Filter) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptICMPv6Filter(fd, level, opt, filter)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptInt wraps setsockopt(2) for integer values.
|
||||
func (c *Conn) SetsockoptInt(level, opt, value int) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptInt(fd, level, opt, value)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptString wraps setsockopt(2) for string values.
|
||||
func (c *Conn) SetsockoptString(level, opt int, value string) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptString(fd, level, opt, value)
|
||||
})
|
||||
}
|
||||
|
||||
// Shutdown wraps shutdown(2).
|
||||
func (c *Conn) Shutdown(how int) error {
|
||||
return c.control("shutdown", func(fd int) error { return unix.Shutdown(fd, how) })
|
||||
}
|
||||
|
||||
// Conn low-level read/write/control functions. These functions mirror the
|
||||
// syscall.RawConn APIs but the input closures return errors rather than
|
||||
// booleans.
|
||||
|
||||
// read wraps readT to execute a function and capture its error result. This is
|
||||
// a convenience wrapper for functions which don't return any extra values.
|
||||
func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := readT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// write executes f, a write function, against the associated file descriptor.
|
||||
// op is used to create an *os.SyscallError if the file descriptor is closed.
|
||||
func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := writeT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// readT executes c.rc.Read for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: read,
|
||||
Op: op,
|
||||
Do: f,
|
||||
})
|
||||
}
|
||||
|
||||
// writeT executes c.rc.Write for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: write,
|
||||
Op: op,
|
||||
Do: f,
|
||||
})
|
||||
}
|
||||
|
||||
// readWrite indicates if an operation intends to read or write.
|
||||
type readWrite bool
|
||||
|
||||
// Possible readWrite values.
|
||||
const (
|
||||
read readWrite = false
|
||||
write readWrite = true
|
||||
)
|
||||
|
||||
// An rwContext provides arguments to rwT.
|
||||
type rwContext[T any] struct {
|
||||
// The caller's context passed for cancelation.
|
||||
Context context.Context
|
||||
|
||||
// The type of an operation: read or write.
|
||||
Type readWrite
|
||||
|
||||
// The name of the operation used in errors.
|
||||
Op string
|
||||
|
||||
// The actual function to perform.
|
||||
Do func(fd int) (T, error)
|
||||
}
|
||||
|
||||
// rwT executes c.rc.Read or c.rc.Write (depending on the value of rw.Type) for
|
||||
// rw.Op using the input function, returning a newly allocated result T.
|
||||
//
|
||||
// It obeys context cancelation and the rw.Context must not be nil.
|
||||
func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
// If the file descriptor is already closed, do nothing.
|
||||
return *new(T), os.NewSyscallError(rw.Op, unix.EBADF)
|
||||
}
|
||||
|
||||
if err := rw.Context.Err(); err != nil {
|
||||
// Early exit due to context cancel.
|
||||
return *new(T), os.NewSyscallError(rw.Op, err)
|
||||
}
|
||||
|
||||
var (
|
||||
// The read or write function used to access the runtime network poller.
|
||||
poll func(func(uintptr) bool) error
|
||||
|
||||
// The read or write function used to set the matching deadline.
|
||||
deadline func(time.Time) error
|
||||
)
|
||||
|
||||
if rw.Type == write {
|
||||
poll = c.rc.Write
|
||||
deadline = c.SetWriteDeadline
|
||||
} else {
|
||||
poll = c.rc.Read
|
||||
deadline = c.SetReadDeadline
|
||||
}
|
||||
|
||||
var (
|
||||
// Whether or not the context carried a deadline we are actively using
|
||||
// for cancelation.
|
||||
setDeadline bool
|
||||
|
||||
// Signals for the cancelation watcher goroutine.
|
||||
wg sync.WaitGroup
|
||||
doneC = make(chan struct{})
|
||||
|
||||
// Atomic: reports whether we have to disarm the deadline.
|
||||
needDisarm atomic.Bool
|
||||
)
|
||||
|
||||
// On cancel, clean up the watcher.
|
||||
defer func() {
|
||||
close(doneC)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
if d, ok := rw.Context.Deadline(); ok {
|
||||
// The context has an explicit deadline. We will use it for cancelation
|
||||
// but disarm it after poll for the next call.
|
||||
if err := deadline(d); err != nil {
|
||||
return *new(T), err
|
||||
}
|
||||
setDeadline = true
|
||||
needDisarm.Store(true)
|
||||
} else {
|
||||
// The context does not have an explicit deadline. We have to watch for
|
||||
// cancelation so we can propagate that signal to immediately unblock
|
||||
// the runtime network poller.
|
||||
//
|
||||
// TODO(mdlayher): is it possible to detect a background context vs a
|
||||
// context with possible future cancel?
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
select {
|
||||
case <-rw.Context.Done():
|
||||
// Cancel the operation. Make the caller disarm after poll
|
||||
// returns.
|
||||
needDisarm.Store(true)
|
||||
_ = deadline(time.Unix(0, 1))
|
||||
case <-doneC:
|
||||
// Nothing to do.
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var (
|
||||
t T
|
||||
err error
|
||||
)
|
||||
|
||||
pollErr := poll(func(fd uintptr) bool {
|
||||
t, err = rw.Do(int(fd))
|
||||
return ready(err)
|
||||
})
|
||||
|
||||
if needDisarm.Load() {
|
||||
_ = deadline(time.Time{})
|
||||
}
|
||||
|
||||
if pollErr != nil {
|
||||
if rw.Context.Err() != nil || (setDeadline && errors.Is(pollErr, os.ErrDeadlineExceeded)) {
|
||||
// The caller canceled the operation or we set a deadline internally
|
||||
// and it was reached.
|
||||
//
|
||||
// Unpack a plain context error. We wait for the context to be done
|
||||
// to synchronize state externally. Otherwise we have noticed I/O
|
||||
// timeout wakeups when we set a deadline but the context was not
|
||||
// yet marked done.
|
||||
<-rw.Context.Done()
|
||||
return *new(T), os.NewSyscallError(rw.Op, rw.Context.Err())
|
||||
}
|
||||
|
||||
// Error from syscall.RawConn methods. Conventionally the standard
|
||||
// library does not wrap internal/poll errors in os.NewSyscallError.
|
||||
return *new(T), pollErr
|
||||
}
|
||||
|
||||
// Result from user function.
|
||||
return t, os.NewSyscallError(rw.Op, err)
|
||||
}
|
||||
|
||||
// control executes Conn.control for op using the input function.
|
||||
func (c *Conn) control(op string, f func(fd int) error) error {
|
||||
_, err := controlT(c, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// controlT executes c.rc.Control for op using the input function, returning a
|
||||
// newly allocated result T.
|
||||
func controlT[T any](c *Conn, op string, f func(fd int) (T, error)) (T, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
// If the file descriptor is already closed, do nothing.
|
||||
return *new(T), os.NewSyscallError(op, unix.EBADF)
|
||||
}
|
||||
|
||||
var (
|
||||
t T
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.rc.Control(func(fd uintptr) {
|
||||
// Repeatedly attempt the syscall(s) invoked by f until completion is
|
||||
// indicated by the return value of ready or the context is canceled.
|
||||
//
|
||||
// The last values for t and err are captured outside of the closure for
|
||||
// use when the loop breaks.
|
||||
for {
|
||||
t, err = f(int(fd))
|
||||
if ready(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
if doErr != nil {
|
||||
// Error from syscall.RawConn methods. Conventionally the standard
|
||||
// library does not wrap internal/poll errors in os.NewSyscallError.
|
||||
return *new(T), doErr
|
||||
}
|
||||
|
||||
// Result from user function.
|
||||
return t, os.NewSyscallError(op, err)
|
||||
}
|
||||
|
||||
// ready indicates readiness based on the value of err.
|
||||
func ready(err error) bool {
|
||||
switch err {
|
||||
case unix.EAGAIN, unix.EINPROGRESS, unix.EINTR:
|
||||
// When a socket is in non-blocking mode, we might see a variety of errors:
|
||||
// - EAGAIN: most common case for a socket read not being ready
|
||||
// - EINPROGRESS: reported by some sockets when first calling connect
|
||||
// - EINTR: system call interrupted, more frequently occurs in Go 1.14+
|
||||
// because goroutines can be asynchronously preempted
|
||||
//
|
||||
// Return false to let the poller wait for readiness. See the source code
|
||||
// for internal/poll.FD.RawRead for more details.
|
||||
return false
|
||||
default:
|
||||
// Ready regardless of whether there was an error or no error.
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Darwin and FreeBSD can't read or write 2GB+ files at a time,
|
||||
// even on 64-bit systems.
|
||||
// The same is true of socket implementations on many systems.
|
||||
// See golang.org/issue/7812 and golang.org/issue/16266.
|
||||
// Use 1GB instead of, say, 2GB-1, to keep subsequent reads aligned.
|
||||
const maxRW = 1 << 30
|
118
vendor/github.com/mdlayher/socket/conn_linux.go
generated
vendored
Normal file
118
vendor/github.com/mdlayher/socket/conn_linux.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IoctlKCMClone wraps ioctl(2) for unix.KCMClone values, but returns a Conn
|
||||
// rather than a raw file descriptor.
|
||||
func (c *Conn) IoctlKCMClone() (*Conn, error) {
|
||||
info, err := controlT(c, "ioctl", unix.IoctlKCMClone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successful clone, wrap in a Conn for use by the caller.
|
||||
return New(int(info.Fd), c.name)
|
||||
}
|
||||
|
||||
// IoctlKCMAttach wraps ioctl(2) for unix.KCMAttach values.
|
||||
func (c *Conn) IoctlKCMAttach(info unix.KCMAttach) error {
|
||||
return c.control("ioctl", func(fd int) error {
|
||||
return unix.IoctlKCMAttach(fd, info)
|
||||
})
|
||||
}
|
||||
|
||||
// IoctlKCMUnattach wraps ioctl(2) for unix.KCMUnattach values.
|
||||
func (c *Conn) IoctlKCMUnattach(info unix.KCMUnattach) error {
|
||||
return c.control("ioctl", func(fd int) error {
|
||||
return unix.IoctlKCMUnattach(fd, info)
|
||||
})
|
||||
}
|
||||
|
||||
// PidfdGetfd wraps pidfd_getfd(2) for a Conn which wraps a pidfd, but returns a
|
||||
// Conn rather than a raw file descriptor.
|
||||
func (c *Conn) PidfdGetfd(targetFD, flags int) (*Conn, error) {
|
||||
outFD, err := controlT(c, "pidfd_getfd", func(fd int) (int, error) {
|
||||
return unix.PidfdGetfd(fd, targetFD, flags)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successful getfd, wrap in a Conn for use by the caller.
|
||||
return New(outFD, c.name)
|
||||
}
|
||||
|
||||
// PidfdSendSignal wraps pidfd_send_signal(2) for a Conn which wraps a Linux
|
||||
// pidfd.
|
||||
func (c *Conn) PidfdSendSignal(sig unix.Signal, info *unix.Siginfo, flags int) error {
|
||||
return c.control("pidfd_send_signal", func(fd int) error {
|
||||
return unix.PidfdSendSignal(fd, sig, info, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// SetBPF attaches an assembled BPF program to a Conn.
|
||||
func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
|
||||
// We can't point to the first instruction in the array if no instructions
|
||||
// are present.
|
||||
if len(filter) == 0 {
|
||||
return os.NewSyscallError("setsockopt", unix.EINVAL)
|
||||
}
|
||||
|
||||
prog := unix.SockFprog{
|
||||
Len: uint16(len(filter)),
|
||||
Filter: (*unix.SockFilter)(unsafe.Pointer(&filter[0])),
|
||||
}
|
||||
|
||||
return c.SetsockoptSockFprog(unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, &prog)
|
||||
}
|
||||
|
||||
// RemoveBPF removes a BPF filter from a Conn.
|
||||
func (c *Conn) RemoveBPF() error {
|
||||
// 0 argument is ignored.
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_DETACH_FILTER, 0)
|
||||
}
|
||||
|
||||
// SetsockoptPacketMreq wraps setsockopt(2) for unix.PacketMreq values.
|
||||
func (c *Conn) SetsockoptPacketMreq(level, opt int, mreq *unix.PacketMreq) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptPacketMreq(fd, level, opt, mreq)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptSockFprog wraps setsockopt(2) for unix.SockFprog values.
|
||||
func (c *Conn) SetsockoptSockFprog(level, opt int, fprog *unix.SockFprog) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptSockFprog(fd, level, opt, fprog)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptTpacketStats wraps getsockopt(2) for unix.TpacketStats values.
|
||||
func (c *Conn) GetsockoptTpacketStats(level, name int) (*unix.TpacketStats, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (*unix.TpacketStats, error) {
|
||||
return unix.GetsockoptTpacketStats(fd, level, name)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptTpacketStatsV3 wraps getsockopt(2) for unix.TpacketStatsV3 values.
|
||||
func (c *Conn) GetsockoptTpacketStatsV3(level, name int) (*unix.TpacketStatsV3, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (*unix.TpacketStatsV3, error) {
|
||||
return unix.GetsockoptTpacketStatsV3(fd, level, name)
|
||||
})
|
||||
}
|
||||
|
||||
// Waitid wraps waitid(2).
|
||||
func (c *Conn) Waitid(idType int, info *unix.Siginfo, options int, rusage *unix.Rusage) error {
|
||||
return c.read(context.Background(), "waitid", func(fd int) error {
|
||||
return unix.Waitid(idType, fd, info, options, rusage)
|
||||
})
|
||||
}
|
13
vendor/github.com/mdlayher/socket/doc.go
generated
vendored
Normal file
13
vendor/github.com/mdlayher/socket/doc.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Package socket provides a low-level network connection type which integrates
|
||||
// with Go's runtime network poller to provide asynchronous I/O and deadline
|
||||
// support.
|
||||
//
|
||||
// This package focuses on UNIX-like operating systems which make use of BSD
|
||||
// sockets system call APIs. It is meant to be used as a foundation for the
|
||||
// creation of operating system-specific socket packages, for socket families
|
||||
// such as Linux's AF_NETLINK, AF_PACKET, or AF_VSOCK. This package should not
|
||||
// be used directly in end user applications.
|
||||
//
|
||||
// Any use of package socket should be guarded by build tags, as one would also
|
||||
// use when importing the syscall or golang.org/x/sys packages.
|
||||
package socket
|
150
vendor/github.com/mdlayher/socket/netns_linux.go
generated
vendored
Normal file
150
vendor/github.com/mdlayher/socket/netns_linux.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// errNetNSDisabled is returned when network namespaces are unavailable on
|
||||
// a given system.
|
||||
var errNetNSDisabled = errors.New("socket: Linux network namespaces are not enabled on this system")
|
||||
|
||||
// withNetNS invokes fn within the context of the network namespace specified by
|
||||
// fd, while also managing the logic required to safely do so by manipulating
|
||||
// thread-local state.
|
||||
func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) {
|
||||
var (
|
||||
eg errgroup.Group
|
||||
conn *Conn
|
||||
)
|
||||
|
||||
eg.Go(func() error {
|
||||
// Retrieve and store the calling OS thread's network namespace so the
|
||||
// thread can be reassigned to it after creating a socket in another network
|
||||
// namespace.
|
||||
runtime.LockOSThread()
|
||||
|
||||
ns, err := threadNetNS()
|
||||
if err != nil {
|
||||
// No thread-local manipulation, unlock.
|
||||
runtime.UnlockOSThread()
|
||||
return err
|
||||
}
|
||||
defer ns.Close()
|
||||
|
||||
// Beyond this point, the thread's network namespace is poisoned. Do not
|
||||
// unlock the OS thread until all network namespace manipulation completes
|
||||
// to avoid returning to the caller with altered thread-local state.
|
||||
|
||||
// Assign the current OS thread the goroutine is locked to to the given
|
||||
// network namespace.
|
||||
if err := ns.Set(fd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt Conn creation and unconditionally restore the original namespace.
|
||||
c, err := fn()
|
||||
if nerr := ns.Restore(); nerr != nil {
|
||||
// Failed to restore original namespace. Return an error and allow the
|
||||
// runtime to terminate the thread.
|
||||
if err == nil {
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
return nerr
|
||||
}
|
||||
|
||||
// No more thread-local state manipulation; return the new Conn.
|
||||
runtime.UnlockOSThread()
|
||||
conn = c
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// A netNS is a handle that can manipulate network namespaces.
|
||||
//
|
||||
// Operations performed on a netNS must use runtime.LockOSThread before
|
||||
// manipulating any network namespaces.
|
||||
type netNS struct {
|
||||
// The handle to a network namespace.
|
||||
f *os.File
|
||||
|
||||
// Indicates if network namespaces are disabled on this system, and thus
|
||||
// operations should become a no-op or return errors.
|
||||
disabled bool
|
||||
}
|
||||
|
||||
// threadNetNS constructs a netNS using the network namespace of the calling
|
||||
// thread. If the namespace is not the default namespace, runtime.LockOSThread
|
||||
// should be invoked first.
|
||||
func threadNetNS() (*netNS, error) {
|
||||
return fileNetNS(fmt.Sprintf("/proc/self/task/%d/ns/net", unix.Gettid()))
|
||||
}
|
||||
|
||||
// fileNetNS opens file and creates a netNS. fileNetNS should only be called
|
||||
// directly in tests.
|
||||
func fileNetNS(file string) (*netNS, error) {
|
||||
f, err := os.Open(file)
|
||||
switch {
|
||||
case err == nil:
|
||||
return &netNS{f: f}, nil
|
||||
case os.IsNotExist(err):
|
||||
// Network namespaces are not enabled on this system. Use this signal
|
||||
// to return errors elsewhere if the caller explicitly asks for a
|
||||
// network namespace to be set.
|
||||
return &netNS{disabled: true}, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Close releases the handle to a network namespace.
|
||||
func (n *netNS) Close() error {
|
||||
return n.do(func() error { return n.f.Close() })
|
||||
}
|
||||
|
||||
// FD returns a file descriptor which represents the network namespace.
|
||||
func (n *netNS) FD() int {
|
||||
if n.disabled {
|
||||
// No reasonable file descriptor value in this case, so specify a
|
||||
// non-existent one.
|
||||
return -1
|
||||
}
|
||||
|
||||
return int(n.f.Fd())
|
||||
}
|
||||
|
||||
// Restore restores the original network namespace for the calling thread.
|
||||
func (n *netNS) Restore() error {
|
||||
return n.do(func() error { return n.Set(n.FD()) })
|
||||
}
|
||||
|
||||
// Set sets a new network namespace for the current thread using fd.
|
||||
func (n *netNS) Set(fd int) error {
|
||||
return n.do(func() error {
|
||||
return os.NewSyscallError("setns", unix.Setns(fd, unix.CLONE_NEWNET))
|
||||
})
|
||||
}
|
||||
|
||||
// do runs fn if network namespaces are enabled on this system.
|
||||
func (n *netNS) do(fn func() error) error {
|
||||
if n.disabled {
|
||||
return errNetNSDisabled
|
||||
}
|
||||
|
||||
return fn()
|
||||
}
|
14
vendor/github.com/mdlayher/socket/netns_others.go
generated
vendored
Normal file
14
vendor/github.com/mdlayher/socket/netns_others.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// withNetNS returns an error on non-Linux systems.
|
||||
func withNetNS(_ int, _ func() (*Conn, error)) (*Conn, error) {
|
||||
return nil, fmt.Errorf("socket: Linux network namespace support is not available on %s", runtime.GOOS)
|
||||
}
|
24
vendor/github.com/mdlayher/socket/setbuffer_linux.go
generated
vendored
Normal file
24
vendor/github.com/mdlayher/socket/setbuffer_linux.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// setReadBuffer wraps the SO_RCVBUF{,FORCE} setsockopt(2) options.
|
||||
func (c *Conn) setReadBuffer(bytes int) error {
|
||||
err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes)
|
||||
if err != nil {
|
||||
err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// setWriteBuffer wraps the SO_SNDBUF{,FORCE} setsockopt(2) options.
|
||||
func (c *Conn) setWriteBuffer(bytes int) error {
|
||||
err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes)
|
||||
if err != nil {
|
||||
err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
|
||||
}
|
||||
return err
|
||||
}
|
16
vendor/github.com/mdlayher/socket/setbuffer_others.go
generated
vendored
Normal file
16
vendor/github.com/mdlayher/socket/setbuffer_others.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// setReadBuffer wraps the SO_RCVBUF setsockopt(2) option.
|
||||
func (c *Conn) setReadBuffer(bytes int) error {
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
|
||||
}
|
||||
|
||||
// setWriteBuffer wraps the SO_SNDBUF setsockopt(2) option.
|
||||
func (c *Conn) setWriteBuffer(bytes int) error {
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
|
||||
}
|
12
vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
generated
vendored
Normal file
12
vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
//go:build !darwin
|
||||
// +build !darwin
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const (
|
||||
// These operating systems support CLOEXEC and NONBLOCK socket options.
|
||||
flagCLOEXEC = true
|
||||
socketFlags = unix.SOCK_CLOEXEC | unix.SOCK_NONBLOCK
|
||||
)
|
11
vendor/github.com/mdlayher/socket/typ_none.go
generated
vendored
Normal file
11
vendor/github.com/mdlayher/socket/typ_none.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package socket
|
||||
|
||||
const (
|
||||
// These operating systems do not support CLOEXEC and NONBLOCK socket
|
||||
// options.
|
||||
flagCLOEXEC = false
|
||||
socketFlags = 0
|
||||
)
|
36
vendor/github.com/pierrec/lz4/v4/.gitignore
generated
vendored
Normal file
36
vendor/github.com/pierrec/lz4/v4/.gitignore
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# Created by https://www.gitignore.io/api/macos
|
||||
|
||||
### macOS ###
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# End of https://www.gitignore.io/api/macos
|
||||
|
||||
cmd/*/*exe
|
||||
.idea
|
||||
|
||||
fuzz/*.zip
|
28
vendor/github.com/pierrec/lz4/v4/LICENSE
generated
vendored
Normal file
28
vendor/github.com/pierrec/lz4/v4/LICENSE
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
Copyright (c) 2015, Pierre Curto
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of xxHash nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
92
vendor/github.com/pierrec/lz4/v4/README.md
generated
vendored
Normal file
92
vendor/github.com/pierrec/lz4/v4/README.md
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
# lz4 : LZ4 compression in pure Go
|
||||
|
||||
[](https://pkg.go.dev/github.com/pierrec/lz4/v4)
|
||||
[](https://github.com/pierrec/lz4/actions)
|
||||
[](https://goreportcard.com/report/github.com/pierrec/lz4)
|
||||
[](https://github.com/pierrec/lz4/tags)
|
||||
|
||||
## Overview
|
||||
|
||||
This package provides a streaming interface to [LZ4 data streams](http://fastcompression.blogspot.fr/2013/04/lz4-streaming-format-final.html) as well as low level compress and uncompress functions for LZ4 data blocks.
|
||||
The implementation is based on the reference C [one](https://github.com/lz4/lz4).
|
||||
|
||||
## Install
|
||||
|
||||
Assuming you have the go toolchain installed:
|
||||
|
||||
```
|
||||
go get github.com/pierrec/lz4/v4
|
||||
```
|
||||
|
||||
There is a command line interface tool to compress and decompress LZ4 files.
|
||||
|
||||
```
|
||||
go install github.com/pierrec/lz4/v4/cmd/lz4c@latest
|
||||
```
|
||||
|
||||
Usage
|
||||
|
||||
```
|
||||
Usage of lz4c:
|
||||
-version
|
||||
print the program version
|
||||
|
||||
Subcommands:
|
||||
Compress the given files or from stdin to stdout.
|
||||
compress [arguments] [<file name> ...]
|
||||
-bc
|
||||
enable block checksum
|
||||
-l int
|
||||
compression level (0=fastest)
|
||||
-sc
|
||||
disable stream checksum
|
||||
-size string
|
||||
block max size [64K,256K,1M,4M] (default "4M")
|
||||
|
||||
Uncompress the given files or from stdin to stdout.
|
||||
uncompress [arguments] [<file name> ...]
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
// Compress and uncompress an input string.
|
||||
s := "hello world"
|
||||
r := strings.NewReader(s)
|
||||
|
||||
// The pipe will uncompress the data from the writer.
|
||||
pr, pw := io.Pipe()
|
||||
zw := lz4.NewWriter(pw)
|
||||
zr := lz4.NewReader(pr)
|
||||
|
||||
go func() {
|
||||
// Compress the input string.
|
||||
_, _ = io.Copy(zw, r)
|
||||
_ = zw.Close() // Make sure the writer is closed
|
||||
_ = pw.Close() // Terminate the pipe
|
||||
}()
|
||||
|
||||
_, _ = io.Copy(os.Stdout, zr)
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are very welcome for bug fixing, performance improvements...!
|
||||
|
||||
- Open an issue with a proper description
|
||||
- Send a pull request with appropriate test case(s)
|
||||
|
||||
## Contributors
|
||||
|
||||
Thanks to all [contributors](https://github.com/pierrec/lz4/graphs/contributors) so far!
|
||||
|
||||
Special thanks to [@Zariel](https://github.com/Zariel) for his asm implementation of the decoder.
|
||||
|
||||
Special thanks to [@greatroar](https://github.com/greatroar) for his work on the asm implementations of the decoder for amd64 and arm64.
|
||||
|
||||
Special thanks to [@klauspost](https://github.com/klauspost) for his work on optimizing the code.
|
222
vendor/github.com/pierrec/lz4/v4/compressing_reader.go
generated
vendored
Normal file
222
vendor/github.com/pierrec/lz4/v4/compressing_reader.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
package lz4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/pierrec/lz4/v4/internal/lz4block"
|
||||
"github.com/pierrec/lz4/v4/internal/lz4errors"
|
||||
"github.com/pierrec/lz4/v4/internal/lz4stream"
|
||||
)
|
||||
|
||||
type crState int
|
||||
|
||||
const (
|
||||
crStateInitial crState = iota
|
||||
crStateReading
|
||||
crStateFlushing
|
||||
crStateDone
|
||||
)
|
||||
|
||||
type CompressingReader struct {
|
||||
state crState
|
||||
src io.ReadCloser // source reader
|
||||
level lz4block.CompressionLevel // how hard to try
|
||||
frame *lz4stream.Frame // frame being built
|
||||
in []byte
|
||||
out ovWriter
|
||||
handler func(int)
|
||||
}
|
||||
|
||||
// NewCompressingReader creates a reader which reads compressed data from
|
||||
// raw stream. This makes it a logical opposite of a normal lz4.Reader.
|
||||
// We require an io.ReadCloser as an underlying source for compatibility
|
||||
// with Go's http.Request.
|
||||
func NewCompressingReader(src io.ReadCloser) *CompressingReader {
|
||||
zrd := &CompressingReader {
|
||||
frame: lz4stream.NewFrame(),
|
||||
}
|
||||
|
||||
_ = zrd.Apply(DefaultBlockSizeOption, DefaultChecksumOption, defaultOnBlockDone)
|
||||
zrd.Reset(src)
|
||||
|
||||
return zrd
|
||||
}
|
||||
|
||||
// Source exposes the underlying source stream for introspection and control.
|
||||
func (zrd *CompressingReader) Source() io.ReadCloser {
|
||||
return zrd.src
|
||||
}
|
||||
|
||||
// Close simply invokes the underlying stream Close method. This method is
|
||||
// provided for the benefit of Go http client/server, which relies on Close
|
||||
// for goroutine termination.
|
||||
func (zrd *CompressingReader) Close() error {
|
||||
return zrd.src.Close()
|
||||
}
|
||||
|
||||
// Apply applies useful options to the lz4 encoder.
|
||||
func (zrd *CompressingReader) Apply(options ...Option) (err error) {
|
||||
if zrd.state != crStateInitial {
|
||||
return lz4errors.ErrOptionClosedOrError
|
||||
}
|
||||
|
||||
zrd.Reset(zrd.src)
|
||||
|
||||
for _, o := range options {
|
||||
if err = o(zrd); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (*CompressingReader) private() {}
|
||||
|
||||
func (zrd *CompressingReader) init() error {
|
||||
zrd.frame.InitW(&zrd.out, 1, false)
|
||||
size := zrd.frame.Descriptor.Flags.BlockSizeIndex()
|
||||
zrd.in = size.Get()
|
||||
return zrd.frame.Descriptor.Write(zrd.frame, &zrd.out)
|
||||
}
|
||||
|
||||
// Read allows reading of lz4 compressed data
|
||||
func (zrd *CompressingReader) Read(p []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
zrd.state = crStateDone
|
||||
}
|
||||
}()
|
||||
|
||||
if !zrd.out.reset(p) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
switch zrd.state {
|
||||
case crStateInitial:
|
||||
err = zrd.init()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
zrd.state = crStateReading
|
||||
case crStateDone:
|
||||
return 0, errors.New("This reader is done")
|
||||
case crStateFlushing:
|
||||
if zrd.out.dataPos > 0 {
|
||||
n = zrd.out.dataPos
|
||||
zrd.out.data = nil
|
||||
zrd.out.dataPos = 0
|
||||
return
|
||||
} else {
|
||||
zrd.state = crStateDone
|
||||
return 0, io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
for zrd.state == crStateReading {
|
||||
block := zrd.frame.Blocks.Block
|
||||
|
||||
var rCount int
|
||||
rCount, err = io.ReadFull(zrd.src, zrd.in)
|
||||
switch err {
|
||||
case nil:
|
||||
err = block.Compress(
|
||||
zrd.frame, zrd.in[ : rCount], zrd.level,
|
||||
).Write(zrd.frame, &zrd.out)
|
||||
zrd.handler(len(block.Data))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if zrd.out.dataPos == len(zrd.out.data) {
|
||||
n = zrd.out.dataPos
|
||||
zrd.out.dataPos = 0
|
||||
zrd.out.data = nil
|
||||
return
|
||||
}
|
||||
case io.EOF, io.ErrUnexpectedEOF: // read may be partial
|
||||
if rCount > 0 {
|
||||
err = block.Compress(
|
||||
zrd.frame, zrd.in[ : rCount], zrd.level,
|
||||
).Write(zrd.frame, &zrd.out)
|
||||
zrd.handler(len(block.Data))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = zrd.frame.CloseW(&zrd.out, 1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
zrd.state = crStateFlushing
|
||||
|
||||
n = zrd.out.dataPos
|
||||
zrd.out.dataPos = 0
|
||||
zrd.out.data = nil
|
||||
return
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = lz4errors.ErrInternalUnhandledState
|
||||
return
|
||||
}
|
||||
|
||||
// Reset makes the stream usable again; mostly handy to reuse lz4 encoder
|
||||
// instances.
|
||||
func (zrd *CompressingReader) Reset(src io.ReadCloser) {
|
||||
zrd.frame.Reset(1)
|
||||
zrd.state = crStateInitial
|
||||
zrd.src = src
|
||||
zrd.out.clear()
|
||||
}
|
||||
|
||||
type ovWriter struct {
|
||||
data []byte
|
||||
ov []byte
|
||||
dataPos int
|
||||
ovPos int
|
||||
}
|
||||
|
||||
func (wr *ovWriter) Write(p []byte) (n int, err error) {
|
||||
count := copy(wr.data[wr.dataPos : ], p)
|
||||
wr.dataPos += count
|
||||
|
||||
if count < len(p) {
|
||||
wr.ov = append(wr.ov, p[count : ]...)
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (wr *ovWriter) reset(out []byte) bool {
|
||||
ovRem := len(wr.ov) - wr.ovPos
|
||||
|
||||
if ovRem >= len(out) {
|
||||
wr.ovPos += copy(out, wr.ov[wr.ovPos : ])
|
||||
return false
|
||||
}
|
||||
|
||||
if ovRem > 0 {
|
||||
copy(out, wr.ov[wr.ovPos : ])
|
||||
wr.ov = wr.ov[ : 0]
|
||||
wr.ovPos = 0
|
||||
wr.dataPos = ovRem
|
||||
} else if wr.ovPos > 0 {
|
||||
wr.ov = wr.ov[ : 0]
|
||||
wr.ovPos = 0
|
||||
wr.dataPos = 0
|
||||
}
|
||||
|
||||
wr.data = out
|
||||
return true
|
||||
}
|
||||
|
||||
func (wr *ovWriter) clear() {
|
||||
wr.data = nil
|
||||
wr.dataPos = 0
|
||||
wr.ov = wr.ov[ : 0]
|
||||
wr.ovPos = 0
|
||||
}
|
481
vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go
generated
vendored
Normal file
481
vendor/github.com/pierrec/lz4/v4/internal/lz4block/block.go
generated
vendored
Normal file
@ -0,0 +1,481 @@
|
||||
package lz4block
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
"sync"
|
||||
|
||||
"github.com/pierrec/lz4/v4/internal/lz4errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// The following constants are used to setup the compression algorithm.
|
||||
minMatch = 4 // the minimum size of the match sequence size (4 bytes)
|
||||
winSizeLog = 16 // LZ4 64Kb window size limit
|
||||
winSize = 1 << winSizeLog
|
||||
winMask = winSize - 1 // 64Kb window of previous data for dependent blocks
|
||||
|
||||
// hashLog determines the size of the hash table used to quickly find a previous match position.
|
||||
// Its value influences the compression speed and memory usage, the lower the faster,
|
||||
// but at the expense of the compression ratio.
|
||||
// 16 seems to be the best compromise for fast compression.
|
||||
hashLog = 16
|
||||
htSize = 1 << hashLog
|
||||
|
||||
mfLimit = 10 + minMatch // The last match cannot start within the last 14 bytes.
|
||||
)
|
||||
|
||||
func recoverBlock(e *error) {
|
||||
if r := recover(); r != nil && *e == nil {
|
||||
*e = lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
}
|
||||
|
||||
// blockHash hashes the lower 6 bytes into a value < htSize.
|
||||
func blockHash(x uint64) uint32 {
|
||||
const prime6bytes = 227718039650203
|
||||
return uint32(((x << (64 - 48)) * prime6bytes) >> (64 - hashLog))
|
||||
}
|
||||
|
||||
func CompressBlockBound(n int) int {
|
||||
return n + n/255 + 16
|
||||
}
|
||||
|
||||
func UncompressBlock(src, dst, dict []byte) (int, error) {
|
||||
if len(src) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if di := decodeBlock(dst, src, dict); di >= 0 {
|
||||
return di, nil
|
||||
}
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
|
||||
type Compressor struct {
|
||||
// Offsets are at most 64kiB, so we can store only the lower 16 bits of
|
||||
// match positions: effectively, an offset from some 64kiB block boundary.
|
||||
//
|
||||
// When we retrieve such an offset, we interpret it as relative to the last
|
||||
// block boundary si &^ 0xffff, or the one before, (si &^ 0xffff) - 0x10000,
|
||||
// depending on which of these is inside the current window. If a table
|
||||
// entry was generated more than 64kiB back in the input, we find out by
|
||||
// inspecting the input stream.
|
||||
table [htSize]uint16
|
||||
|
||||
// Bitmap indicating which positions in the table are in use.
|
||||
// This allows us to quickly reset the table for reuse,
|
||||
// without having to zero everything.
|
||||
inUse [htSize / 32]uint32
|
||||
}
|
||||
|
||||
// Get returns the position of a presumptive match for the hash h.
|
||||
// The match may be a false positive due to a hash collision or an old entry.
|
||||
// If si < winSize, the return value may be negative.
|
||||
func (c *Compressor) get(h uint32, si int) int {
|
||||
h &= htSize - 1
|
||||
i := 0
|
||||
if c.inUse[h/32]&(1<<(h%32)) != 0 {
|
||||
i = int(c.table[h])
|
||||
}
|
||||
i += si &^ winMask
|
||||
if i >= si {
|
||||
// Try previous 64kiB block (negative when in first block).
|
||||
i -= winSize
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (c *Compressor) put(h uint32, si int) {
|
||||
h &= htSize - 1
|
||||
c.table[h] = uint16(si)
|
||||
c.inUse[h/32] |= 1 << (h % 32)
|
||||
}
|
||||
|
||||
func (c *Compressor) reset() { c.inUse = [htSize / 32]uint32{} }
|
||||
|
||||
var compressorPool = sync.Pool{New: func() interface{} { return new(Compressor) }}
|
||||
|
||||
func CompressBlock(src, dst []byte) (int, error) {
|
||||
c := compressorPool.Get().(*Compressor)
|
||||
n, err := c.CompressBlock(src, dst)
|
||||
compressorPool.Put(c)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *Compressor) CompressBlock(src, dst []byte) (int, error) {
|
||||
// Zero out reused table to avoid non-deterministic output (issue #65).
|
||||
c.reset()
|
||||
|
||||
// Return 0, nil only if the destination buffer size is < CompressBlockBound.
|
||||
isNotCompressible := len(dst) < CompressBlockBound(len(src))
|
||||
|
||||
// adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible.
|
||||
// This significantly speeds up incompressible data and usually has very small impact on compression.
|
||||
// bytes to skip = 1 + (bytes since last match >> adaptSkipLog)
|
||||
const adaptSkipLog = 7
|
||||
|
||||
// si: Current position of the search.
|
||||
// anchor: Position of the current literals.
|
||||
var si, di, anchor int
|
||||
sn := len(src) - mfLimit
|
||||
if sn <= 0 {
|
||||
goto lastLiterals
|
||||
}
|
||||
|
||||
// Fast scan strategy: the hash table only stores the last 4 bytes sequences.
|
||||
for si < sn {
|
||||
// Hash the next 6 bytes (sequence)...
|
||||
match := binary.LittleEndian.Uint64(src[si:])
|
||||
h := blockHash(match)
|
||||
h2 := blockHash(match >> 8)
|
||||
|
||||
// We check a match at s, s+1 and s+2 and pick the first one we get.
|
||||
// Checking 3 only requires us to load the source one.
|
||||
ref := c.get(h, si)
|
||||
ref2 := c.get(h2, si+1)
|
||||
c.put(h, si)
|
||||
c.put(h2, si+1)
|
||||
|
||||
offset := si - ref
|
||||
|
||||
if offset <= 0 || offset >= winSize || uint32(match) != binary.LittleEndian.Uint32(src[ref:]) {
|
||||
// No match. Start calculating another hash.
|
||||
// The processor can usually do this out-of-order.
|
||||
h = blockHash(match >> 16)
|
||||
ref3 := c.get(h, si+2)
|
||||
|
||||
// Check the second match at si+1
|
||||
si += 1
|
||||
offset = si - ref2
|
||||
|
||||
if offset <= 0 || offset >= winSize || uint32(match>>8) != binary.LittleEndian.Uint32(src[ref2:]) {
|
||||
// No match. Check the third match at si+2
|
||||
si += 1
|
||||
offset = si - ref3
|
||||
c.put(h, si)
|
||||
|
||||
if offset <= 0 || offset >= winSize || uint32(match>>16) != binary.LittleEndian.Uint32(src[ref3:]) {
|
||||
// Skip one extra byte (at si+3) before we check 3 matches again.
|
||||
si += 2 + (si-anchor)>>adaptSkipLog
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Match found.
|
||||
lLen := si - anchor // Literal length.
|
||||
// We already matched 4 bytes.
|
||||
mLen := 4
|
||||
|
||||
// Extend backwards if we can, reducing literals.
|
||||
tOff := si - offset - 1
|
||||
for lLen > 0 && tOff >= 0 && src[si-1] == src[tOff] {
|
||||
si--
|
||||
tOff--
|
||||
lLen--
|
||||
mLen++
|
||||
}
|
||||
|
||||
// Add the match length, so we continue search at the end.
|
||||
// Use mLen to store the offset base.
|
||||
si, mLen = si+mLen, si+minMatch
|
||||
|
||||
// Find the longest match by looking by batches of 8 bytes.
|
||||
for si+8 <= sn {
|
||||
x := binary.LittleEndian.Uint64(src[si:]) ^ binary.LittleEndian.Uint64(src[si-offset:])
|
||||
if x == 0 {
|
||||
si += 8
|
||||
} else {
|
||||
// Stop is first non-zero byte.
|
||||
si += bits.TrailingZeros64(x) >> 3
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
mLen = si - mLen
|
||||
if di >= len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
if mLen < 0xF {
|
||||
dst[di] = byte(mLen)
|
||||
} else {
|
||||
dst[di] = 0xF
|
||||
}
|
||||
|
||||
// Encode literals length.
|
||||
if lLen < 0xF {
|
||||
dst[di] |= byte(lLen << 4)
|
||||
} else {
|
||||
dst[di] |= 0xF0
|
||||
di++
|
||||
l := lLen - 0xF
|
||||
for ; l >= 0xFF && di < len(dst); l -= 0xFF {
|
||||
dst[di] = 0xFF
|
||||
di++
|
||||
}
|
||||
if di >= len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
dst[di] = byte(l)
|
||||
}
|
||||
di++
|
||||
|
||||
// Literals.
|
||||
if di+lLen > len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
copy(dst[di:di+lLen], src[anchor:anchor+lLen])
|
||||
di += lLen + 2
|
||||
anchor = si
|
||||
|
||||
// Encode offset.
|
||||
if di > len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
|
||||
|
||||
// Encode match length part 2.
|
||||
if mLen >= 0xF {
|
||||
for mLen -= 0xF; mLen >= 0xFF && di < len(dst); mLen -= 0xFF {
|
||||
dst[di] = 0xFF
|
||||
di++
|
||||
}
|
||||
if di >= len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
dst[di] = byte(mLen)
|
||||
di++
|
||||
}
|
||||
// Check if we can load next values.
|
||||
if si >= sn {
|
||||
break
|
||||
}
|
||||
// Hash match end-2
|
||||
h = blockHash(binary.LittleEndian.Uint64(src[si-2:]))
|
||||
c.put(h, si-2)
|
||||
}
|
||||
|
||||
lastLiterals:
|
||||
if isNotCompressible && anchor == 0 {
|
||||
// Incompressible.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Last literals.
|
||||
if di >= len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
lLen := len(src) - anchor
|
||||
if lLen < 0xF {
|
||||
dst[di] = byte(lLen << 4)
|
||||
} else {
|
||||
dst[di] = 0xF0
|
||||
di++
|
||||
for lLen -= 0xF; lLen >= 0xFF && di < len(dst); lLen -= 0xFF {
|
||||
dst[di] = 0xFF
|
||||
di++
|
||||
}
|
||||
if di >= len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
dst[di] = byte(lLen)
|
||||
}
|
||||
di++
|
||||
|
||||
// Write the last literals.
|
||||
if isNotCompressible && di >= anchor {
|
||||
// Incompressible.
|
||||
return 0, nil
|
||||
}
|
||||
if di+len(src)-anchor > len(dst) {
|
||||
return 0, lz4errors.ErrInvalidSourceShortBuffer
|
||||
}
|
||||
di += copy(dst[di:di+len(src)-anchor], src[anchor:])
|
||||
return di, nil
|
||||
}
|
||||
|
||||
// blockHash hashes 4 bytes into a value < winSize.
|
||||
func blockHashHC(x uint32) uint32 {
|
||||
const hasher uint32 = 2654435761 // Knuth multiplicative hash.
|
||||
return x * hasher >> (32 - winSizeLog)
|
||||
}
|
||||
|
||||
type CompressorHC struct {
|
||||
// hashTable: stores the last position found for a given hash
|
||||
// chainTable: stores previous positions for a given hash
|
||||
hashTable, chainTable [htSize]int
|
||||
needsReset bool
|
||||
}
|
||||
|
||||
var compressorHCPool = sync.Pool{New: func() interface{} { return new(CompressorHC) }}
|
||||
|
||||
func CompressBlockHC(src, dst []byte, depth CompressionLevel) (int, error) {
|
||||
c := compressorHCPool.Get().(*CompressorHC)
|
||||
n, err := c.CompressBlock(src, dst, depth)
|
||||
compressorHCPool.Put(c)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *CompressorHC) CompressBlock(src, dst []byte, depth CompressionLevel) (_ int, err error) {
|
||||
if c.needsReset {
|
||||
// Zero out reused table to avoid non-deterministic output (issue #65).
|
||||
c.hashTable = [htSize]int{}
|
||||
c.chainTable = [htSize]int{}
|
||||
}
|
||||
c.needsReset = true // Only false on first call.
|
||||
|
||||
defer recoverBlock(&err)
|
||||
|
||||
// Return 0, nil only if the destination buffer size is < CompressBlockBound.
|
||||
isNotCompressible := len(dst) < CompressBlockBound(len(src))
|
||||
|
||||
// adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible.
|
||||
// This significantly speeds up incompressible data and usually has very small impact on compression.
|
||||
// bytes to skip = 1 + (bytes since last match >> adaptSkipLog)
|
||||
const adaptSkipLog = 7
|
||||
|
||||
var si, di, anchor int
|
||||
sn := len(src) - mfLimit
|
||||
if sn <= 0 {
|
||||
goto lastLiterals
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
depth = winSize
|
||||
}
|
||||
|
||||
for si < sn {
|
||||
// Hash the next 4 bytes (sequence).
|
||||
match := binary.LittleEndian.Uint32(src[si:])
|
||||
h := blockHashHC(match)
|
||||
|
||||
// Follow the chain until out of window and give the longest match.
|
||||
mLen := 0
|
||||
offset := 0
|
||||
for next, try := c.hashTable[h], depth; try > 0 && next > 0 && si-next < winSize; next, try = c.chainTable[next&winMask], try-1 {
|
||||
// The first (mLen==0) or next byte (mLen>=minMatch) at current match length
|
||||
// must match to improve on the match length.
|
||||
if src[next+mLen] != src[si+mLen] {
|
||||
continue
|
||||
}
|
||||
ml := 0
|
||||
// Compare the current position with a previous with the same hash.
|
||||
for ml < sn-si {
|
||||
x := binary.LittleEndian.Uint64(src[next+ml:]) ^ binary.LittleEndian.Uint64(src[si+ml:])
|
||||
if x == 0 {
|
||||
ml += 8
|
||||
} else {
|
||||
// Stop is first non-zero byte.
|
||||
ml += bits.TrailingZeros64(x) >> 3
|
||||
break
|
||||
}
|
||||
}
|
||||
if ml < minMatch || ml <= mLen {
|
||||
// Match too small (<minMath) or smaller than the current match.
|
||||
continue
|
||||
}
|
||||
// Found a longer match, keep its position and length.
|
||||
mLen = ml
|
||||
offset = si - next
|
||||
// Try another previous position with the same hash.
|
||||
}
|
||||
c.chainTable[si&winMask] = c.hashTable[h]
|
||||
c.hashTable[h] = si
|
||||
|
||||
// No match found.
|
||||
if mLen == 0 {
|
||||
si += 1 + (si-anchor)>>adaptSkipLog
|
||||
continue
|
||||
}
|
||||
|
||||
// Match found.
|
||||
// Update hash/chain tables with overlapping bytes:
|
||||
// si already hashed, add everything from si+1 up to the match length.
|
||||
winStart := si + 1
|
||||
if ws := si + mLen - winSize; ws > winStart {
|
||||
winStart = ws
|
||||
}
|
||||
for si, ml := winStart, si+mLen; si < ml; {
|
||||
match >>= 8
|
||||
match |= uint32(src[si+3]) << 24
|
||||
h := blockHashHC(match)
|
||||
c.chainTable[si&winMask] = c.hashTable[h]
|
||||
c.hashTable[h] = si
|
||||
si++
|
||||
}
|
||||
|
||||
lLen := si - anchor
|
||||
si += mLen
|
||||
mLen -= minMatch // Match length does not include minMatch.
|
||||
|
||||
if mLen < 0xF {
|
||||
dst[di] = byte(mLen)
|
||||
} else {
|
||||
dst[di] = 0xF
|
||||
}
|
||||
|
||||
// Encode literals length.
|
||||
if lLen < 0xF {
|
||||
dst[di] |= byte(lLen << 4)
|
||||
} else {
|
||||
dst[di] |= 0xF0
|
||||
di++
|
||||
l := lLen - 0xF
|
||||
for ; l >= 0xFF; l -= 0xFF {
|
||||
dst[di] = 0xFF
|
||||
di++
|
||||
}
|
||||
dst[di] = byte(l)
|
||||
}
|
||||
di++
|
||||
|
||||
// Literals.
|
||||
copy(dst[di:di+lLen], src[anchor:anchor+lLen])
|
||||
di += lLen
|
||||
anchor = si
|
||||
|
||||
// Encode offset.
|
||||
di += 2
|
||||
dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
|
||||
|
||||
// Encode match length part 2.
|
||||
if mLen >= 0xF {
|
||||
for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
|
||||
dst[di] = 0xFF
|
||||
di++
|
||||
}
|
||||
dst[di] = byte(mLen)
|
||||
di++
|
||||
}
|
||||
}
|
||||
|
||||
if isNotCompressible && anchor == 0 {
|
||||
// Incompressible.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Last literals.
|
||||
lastLiterals:
|
||||
lLen := len(src) - anchor
|
||||
if lLen < 0xF {
|
||||
dst[di] = byte(lLen << 4)
|
||||
} else {
|
||||
dst[di] = 0xF0
|
||||
di++
|
||||
lLen -= 0xF
|
||||
for ; lLen >= 0xFF; lLen -= 0xFF {
|
||||
dst[di] = 0xFF
|
||||
di++
|
||||
}
|
||||
dst[di] = byte(lLen)
|
||||
}
|
||||
di++
|
||||
|
||||
// Write the last literals.
|
||||
if isNotCompressible && di >= anchor {
|
||||
// Incompressible.
|
||||
return 0, nil
|
||||
}
|
||||
di += copy(dst[di:di+len(src)-anchor], src[anchor:])
|
||||
return di, nil
|
||||
}
|
87
vendor/github.com/pierrec/lz4/v4/internal/lz4block/blocks.go
generated
vendored
Normal file
87
vendor/github.com/pierrec/lz4/v4/internal/lz4block/blocks.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
// Package lz4block provides LZ4 BlockSize types and pools of buffers.
|
||||
package lz4block
|
||||
|
||||
import "sync"
|
||||
|
||||
const (
|
||||
Block64Kb uint32 = 1 << (16 + iota*2)
|
||||
Block256Kb
|
||||
Block1Mb
|
||||
Block4Mb
|
||||
Block8Mb = 2 * Block4Mb
|
||||
)
|
||||
|
||||
var (
|
||||
BlockPool64K = sync.Pool{New: func() interface{} { return make([]byte, Block64Kb) }}
|
||||
BlockPool256K = sync.Pool{New: func() interface{} { return make([]byte, Block256Kb) }}
|
||||
BlockPool1M = sync.Pool{New: func() interface{} { return make([]byte, Block1Mb) }}
|
||||
BlockPool4M = sync.Pool{New: func() interface{} { return make([]byte, Block4Mb) }}
|
||||
BlockPool8M = sync.Pool{New: func() interface{} { return make([]byte, Block8Mb) }}
|
||||
)
|
||||
|
||||
func Index(b uint32) BlockSizeIndex {
|
||||
switch b {
|
||||
case Block64Kb:
|
||||
return 4
|
||||
case Block256Kb:
|
||||
return 5
|
||||
case Block1Mb:
|
||||
return 6
|
||||
case Block4Mb:
|
||||
return 7
|
||||
case Block8Mb: // only valid in legacy mode
|
||||
return 3
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func IsValid(b uint32) bool {
|
||||
return Index(b) > 0
|
||||
}
|
||||
|
||||
type BlockSizeIndex uint8
|
||||
|
||||
func (b BlockSizeIndex) IsValid() bool {
|
||||
switch b {
|
||||
case 4, 5, 6, 7:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b BlockSizeIndex) Get() []byte {
|
||||
var buf interface{}
|
||||
switch b {
|
||||
case 4:
|
||||
buf = BlockPool64K.Get()
|
||||
case 5:
|
||||
buf = BlockPool256K.Get()
|
||||
case 6:
|
||||
buf = BlockPool1M.Get()
|
||||
case 7:
|
||||
buf = BlockPool4M.Get()
|
||||
case 3:
|
||||
buf = BlockPool8M.Get()
|
||||
}
|
||||
return buf.([]byte)
|
||||
}
|
||||
|
||||
func Put(buf []byte) {
|
||||
// Safeguard: do not allow invalid buffers.
|
||||
switch c := cap(buf); uint32(c) {
|
||||
case Block64Kb:
|
||||
BlockPool64K.Put(buf[:c])
|
||||
case Block256Kb:
|
||||
BlockPool256K.Put(buf[:c])
|
||||
case Block1Mb:
|
||||
BlockPool1M.Put(buf[:c])
|
||||
case Block4Mb:
|
||||
BlockPool4M.Put(buf[:c])
|
||||
case Block8Mb:
|
||||
BlockPool8M.Put(buf[:c])
|
||||
}
|
||||
}
|
||||
|
||||
type CompressionLevel uint32
|
||||
|
||||
const Fast CompressionLevel = 0
|
448
vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s
generated
vendored
Normal file
448
vendor/github.com/pierrec/lz4/v4/internal/lz4block/decode_amd64.s
generated
vendored
Normal file
@ -0,0 +1,448 @@
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
// AX scratch
|
||||
// BX scratch
|
||||
// CX literal and match lengths
|
||||
// DX token, match offset
|
||||
//
|
||||
// DI &dst
|
||||
// SI &src
|
||||
// R8 &dst + len(dst)
|
||||
// R9 &src + len(src)
|
||||
// R11 &dst
|
||||
// R12 short output end
|
||||
// R13 short input end
|
||||
// R14 &dict
|
||||
// R15 len(dict)
|
||||
|
||||
// func decodeBlock(dst, src, dict []byte) int
|
||||
TEXT ·decodeBlock(SB), NOSPLIT, $48-80
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ DI, R11
|
||||
MOVQ dst_len+8(FP), R8
|
||||
ADDQ DI, R8
|
||||
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ src_len+32(FP), R9
|
||||
CMPQ R9, $0
|
||||
JE err_corrupt
|
||||
ADDQ SI, R9
|
||||
|
||||
MOVQ dict_base+48(FP), R14
|
||||
MOVQ dict_len+56(FP), R15
|
||||
|
||||
// shortcut ends
|
||||
// short output end
|
||||
MOVQ R8, R12
|
||||
SUBQ $32, R12
|
||||
// short input end
|
||||
MOVQ R9, R13
|
||||
SUBQ $16, R13
|
||||
|
||||
XORL CX, CX
|
||||
|
||||
loop:
|
||||
// token := uint32(src[si])
|
||||
MOVBLZX (SI), DX
|
||||
INCQ SI
|
||||
|
||||
// lit_len = token >> 4
|
||||
// if lit_len > 0
|
||||
// CX = lit_len
|
||||
MOVL DX, CX
|
||||
SHRL $4, CX
|
||||
|
||||
// if lit_len != 0xF
|
||||
CMPL CX, $0xF
|
||||
JEQ lit_len_loop
|
||||
CMPQ DI, R12
|
||||
JAE copy_literal
|
||||
CMPQ SI, R13
|
||||
JAE copy_literal
|
||||
|
||||
// copy shortcut
|
||||
|
||||
// A two-stage shortcut for the most common case:
|
||||
// 1) If the literal length is 0..14, and there is enough space,
|
||||
// enter the shortcut and copy 16 bytes on behalf of the literals
|
||||
// (in the fast mode, only 8 bytes can be safely copied this way).
|
||||
// 2) Further if the match length is 4..18, copy 18 bytes in a similar
|
||||
// manner; but we ensure that there's enough space in the output for
|
||||
// those 18 bytes earlier, upon entering the shortcut (in other words,
|
||||
// there is a combined check for both stages).
|
||||
|
||||
// copy literal
|
||||
MOVOU (SI), X0
|
||||
MOVOU X0, (DI)
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
|
||||
MOVL DX, CX
|
||||
ANDL $0xF, CX
|
||||
|
||||
// The second stage: prepare for match copying, decode full info.
|
||||
// If it doesn't work out, the info won't be wasted.
|
||||
// offset := uint16(data[:2])
|
||||
MOVWLZX (SI), DX
|
||||
TESTL DX, DX
|
||||
JE err_corrupt
|
||||
ADDQ $2, SI
|
||||
JC err_short_buf
|
||||
|
||||
MOVQ DI, AX
|
||||
SUBQ DX, AX
|
||||
JC err_corrupt
|
||||
CMPQ AX, DI
|
||||
JA err_short_buf
|
||||
|
||||
// if we can't do the second stage then jump straight to read the
|
||||
// match length, we already have the offset.
|
||||
CMPL CX, $0xF
|
||||
JEQ match_len_loop_pre
|
||||
CMPL DX, $8
|
||||
JLT match_len_loop_pre
|
||||
CMPQ AX, R11
|
||||
JB match_len_loop_pre
|
||||
|
||||
// memcpy(op + 0, match + 0, 8);
|
||||
MOVQ (AX), BX
|
||||
MOVQ BX, (DI)
|
||||
// memcpy(op + 8, match + 8, 8);
|
||||
MOVQ 8(AX), BX
|
||||
MOVQ BX, 8(DI)
|
||||
// memcpy(op +16, match +16, 2);
|
||||
MOVW 16(AX), BX
|
||||
MOVW BX, 16(DI)
|
||||
|
||||
LEAQ const_minMatch(DI)(CX*1), DI
|
||||
|
||||
// shortcut complete, load next token
|
||||
JMP loopcheck
|
||||
|
||||
// Read the rest of the literal length:
|
||||
// do { BX = src[si++]; lit_len += BX } while (BX == 0xFF).
|
||||
lit_len_loop:
|
||||
CMPQ SI, R9
|
||||
JAE err_short_buf
|
||||
|
||||
MOVBLZX (SI), BX
|
||||
INCQ SI
|
||||
ADDQ BX, CX
|
||||
|
||||
CMPB BX, $0xFF
|
||||
JE lit_len_loop
|
||||
|
||||
copy_literal:
|
||||
// bounds check src and dst
|
||||
MOVQ SI, AX
|
||||
ADDQ CX, AX
|
||||
JC err_short_buf
|
||||
CMPQ AX, R9
|
||||
JA err_short_buf
|
||||
|
||||
MOVQ DI, BX
|
||||
ADDQ CX, BX
|
||||
JC err_short_buf
|
||||
CMPQ BX, R8
|
||||
JA err_short_buf
|
||||
|
||||
// Copy literals of <=48 bytes through the XMM registers.
|
||||
CMPQ CX, $48
|
||||
JGT memmove_lit
|
||||
|
||||
// if len(dst[di:]) < 48
|
||||
MOVQ R8, AX
|
||||
SUBQ DI, AX
|
||||
CMPQ AX, $48
|
||||
JLT memmove_lit
|
||||
|
||||
// if len(src[si:]) < 48
|
||||
MOVQ R9, BX
|
||||
SUBQ SI, BX
|
||||
CMPQ BX, $48
|
||||
JLT memmove_lit
|
||||
|
||||
MOVOU (SI), X0
|
||||
MOVOU 16(SI), X1
|
||||
MOVOU 32(SI), X2
|
||||
MOVOU X0, (DI)
|
||||
MOVOU X1, 16(DI)
|
||||
MOVOU X2, 32(DI)
|
||||
|
||||
ADDQ CX, SI
|
||||
ADDQ CX, DI
|
||||
|
||||
JMP finish_lit_copy
|
||||
|
||||
memmove_lit:
|
||||
// memmove(to, from, len)
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ SI, 8(SP)
|
||||
MOVQ CX, 16(SP)
|
||||
|
||||
// Spill registers. Increment SI, DI now so we don't need to save CX.
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
MOVQ DI, 24(SP)
|
||||
MOVQ SI, 32(SP)
|
||||
MOVL DX, 40(SP)
|
||||
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// restore registers
|
||||
MOVQ 24(SP), DI
|
||||
MOVQ 32(SP), SI
|
||||
MOVL 40(SP), DX
|
||||
|
||||
// recalc initial values
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ R8, R11
|
||||
ADDQ dst_len+8(FP), R8
|
||||
MOVQ src_base+24(FP), R9
|
||||
ADDQ src_len+32(FP), R9
|
||||
MOVQ dict_base+48(FP), R14
|
||||
MOVQ dict_len+56(FP), R15
|
||||
MOVQ R8, R12
|
||||
SUBQ $32, R12
|
||||
MOVQ R9, R13
|
||||
SUBQ $16, R13
|
||||
|
||||
finish_lit_copy:
|
||||
// CX := mLen
|
||||
// free up DX to use for offset
|
||||
MOVL DX, CX
|
||||
ANDL $0xF, CX
|
||||
|
||||
CMPQ SI, R9
|
||||
JAE end
|
||||
|
||||
// offset
|
||||
// si += 2
|
||||
// DX := int(src[si-2]) | int(src[si-1])<<8
|
||||
ADDQ $2, SI
|
||||
JC err_short_buf
|
||||
CMPQ SI, R9
|
||||
JA err_short_buf
|
||||
MOVWQZX -2(SI), DX
|
||||
|
||||
// 0 offset is invalid
|
||||
TESTL DX, DX
|
||||
JEQ err_corrupt
|
||||
|
||||
match_len_loop_pre:
|
||||
// if mlen != 0xF
|
||||
CMPB CX, $0xF
|
||||
JNE copy_match
|
||||
|
||||
// do { BX = src[si++]; mlen += BX } while (BX == 0xFF).
|
||||
match_len_loop:
|
||||
CMPQ SI, R9
|
||||
JAE err_short_buf
|
||||
|
||||
MOVBLZX (SI), BX
|
||||
INCQ SI
|
||||
ADDQ BX, CX
|
||||
|
||||
CMPB BX, $0xFF
|
||||
JE match_len_loop
|
||||
|
||||
copy_match:
|
||||
ADDQ $const_minMatch, CX
|
||||
|
||||
// check we have match_len bytes left in dst
|
||||
// di+match_len < len(dst)
|
||||
MOVQ DI, AX
|
||||
ADDQ CX, AX
|
||||
JC err_short_buf
|
||||
CMPQ AX, R8
|
||||
JA err_short_buf
|
||||
|
||||
// DX = offset
|
||||
// CX = match_len
|
||||
// BX = &dst + (di - offset)
|
||||
MOVQ DI, BX
|
||||
SUBQ DX, BX
|
||||
|
||||
// check BX is within dst
|
||||
// if BX < &dst
|
||||
JC copy_match_from_dict
|
||||
CMPQ BX, R11
|
||||
JBE copy_match_from_dict
|
||||
|
||||
// if offset + match_len < di
|
||||
LEAQ (BX)(CX*1), AX
|
||||
CMPQ DI, AX
|
||||
JA copy_interior_match
|
||||
|
||||
// AX := len(dst[:di])
|
||||
// MOVQ DI, AX
|
||||
// SUBQ R11, AX
|
||||
|
||||
// copy 16 bytes at a time
|
||||
// if di-offset < 16 copy 16-(di-offset) bytes to di
|
||||
// then do the remaining
|
||||
|
||||
copy_match_loop:
|
||||
// for match_len >= 0
|
||||
// dst[di] = dst[i]
|
||||
// di++
|
||||
// i++
|
||||
MOVB (BX), AX
|
||||
MOVB AX, (DI)
|
||||
INCQ DI
|
||||
INCQ BX
|
||||
DECQ CX
|
||||
JNZ copy_match_loop
|
||||
|
||||
JMP loopcheck
|
||||
|
||||
copy_interior_match:
|
||||
CMPQ CX, $16
|
||||
JGT memmove_match
|
||||
|
||||
// if len(dst[di:]) < 16
|
||||
MOVQ R8, AX
|
||||
SUBQ DI, AX
|
||||
CMPQ AX, $16
|
||||
JLT memmove_match
|
||||
|
||||
MOVOU (BX), X0
|
||||
MOVOU X0, (DI)
|
||||
|
||||
ADDQ CX, DI
|
||||
XORL CX, CX
|
||||
JMP loopcheck
|
||||
|
||||
copy_match_from_dict:
|
||||
// CX = match_len
|
||||
// BX = &dst + (di - offset)
|
||||
|
||||
// AX = offset - di = dict_bytes_available => count of bytes potentially covered by the dictionary
|
||||
MOVQ R11, AX
|
||||
SUBQ BX, AX
|
||||
|
||||
// BX = len(dict) - dict_bytes_available
|
||||
MOVQ R15, BX
|
||||
SUBQ AX, BX
|
||||
JS err_short_dict
|
||||
|
||||
ADDQ R14, BX
|
||||
|
||||
// if match_len > dict_bytes_available, match fits entirely within external dictionary : just copy
|
||||
CMPQ CX, AX
|
||||
JLT memmove_match
|
||||
|
||||
// The match stretches over the dictionary and our block
|
||||
// 1) copy what comes from the dictionary
|
||||
// AX = dict_bytes_available = copy_size
|
||||
// BX = &dict_end - copy_size
|
||||
// CX = match_len
|
||||
|
||||
// memmove(to, from, len)
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ BX, 8(SP)
|
||||
MOVQ AX, 16(SP)
|
||||
// store extra stuff we want to recover
|
||||
// spill
|
||||
MOVQ DI, 24(SP)
|
||||
MOVQ SI, 32(SP)
|
||||
MOVQ CX, 40(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// restore registers
|
||||
MOVQ 16(SP), AX // copy_size
|
||||
MOVQ 24(SP), DI
|
||||
MOVQ 32(SP), SI
|
||||
MOVQ 40(SP), CX // match_len
|
||||
|
||||
// recalc initial values
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ R8, R11 // TODO: make these sensible numbers
|
||||
ADDQ dst_len+8(FP), R8
|
||||
MOVQ src_base+24(FP), R9
|
||||
ADDQ src_len+32(FP), R9
|
||||
MOVQ dict_base+48(FP), R14
|
||||
MOVQ dict_len+56(FP), R15
|
||||
MOVQ R8, R12
|
||||
SUBQ $32, R12
|
||||
MOVQ R9, R13
|
||||
SUBQ $16, R13
|
||||
|
||||
// di+=copy_size
|
||||
ADDQ AX, DI
|
||||
|
||||
// 2) copy the rest from the current block
|
||||
// CX = match_len - copy_size = rest_size
|
||||
SUBQ AX, CX
|
||||
MOVQ R11, BX
|
||||
|
||||
// check if we have a copy overlap
|
||||
// AX = &dst + rest_size
|
||||
MOVQ CX, AX
|
||||
ADDQ BX, AX
|
||||
// if &dst + rest_size > di, copy byte by byte
|
||||
CMPQ AX, DI
|
||||
|
||||
JA copy_match_loop
|
||||
|
||||
memmove_match:
|
||||
// memmove(to, from, len)
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ BX, 8(SP)
|
||||
MOVQ CX, 16(SP)
|
||||
|
||||
// Spill registers. Increment DI now so we don't need to save CX.
|
||||
ADDQ CX, DI
|
||||
MOVQ DI, 24(SP)
|
||||
MOVQ SI, 32(SP)
|
||||
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// restore registers
|
||||
MOVQ 24(SP), DI
|
||||
MOVQ 32(SP), SI
|
||||
|
||||
// recalc initial values
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ R8, R11 // TODO: make these sensible numbers
|
||||
ADDQ dst_len+8(FP), R8
|
||||
MOVQ src_base+24(FP), R9
|
||||
ADDQ src_len+32(FP), R9
|
||||
MOVQ R8, R12
|
||||
SUBQ $32, R12
|
||||
MOVQ R9, R13
|
||||
SUBQ $16, R13
|
||||
MOVQ dict_base+48(FP), R14
|
||||
MOVQ dict_len+56(FP), R15
|
||||
XORL CX, CX
|
||||
|
||||
loopcheck:
|
||||
// for si < len(src)
|
||||
CMPQ SI, R9
|
||||
JB loop
|
||||
|
||||
end:
|
||||
// Remaining length must be zero.
|
||||
TESTQ CX, CX
|
||||
JNE err_corrupt
|
||||
|
||||
SUBQ R11, DI
|
||||
MOVQ DI, ret+72(FP)
|
||||
RET
|
||||
|
||||
err_corrupt:
|
||||
MOVQ $-1, ret+72(FP)
|
||||
RET
|
||||
|
||||
err_short_buf:
|
||||
MOVQ $-2, ret+72(FP)
|
||||
RET
|
||||
|
||||
err_short_dict:
|
||||
MOVQ $-3, ret+72(FP)
|
||||
RET
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user