mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
move from Godeps to vndr
This uses the standard go vendor location instead of old Godeps location. Also remove usage of symlink GOPATH. Since our README mentions that you should build it inside GOPATH, i think its a reasonable to assume that you dont need to create a tmp GOPATH. Signed-off-by: Daniel Dao <dqminh89@gmail.com>
This commit is contained in:
committed by
Daniel Dao
parent
bd2f9c52cd
commit
993cbf9db0
+192
@@ -0,0 +1,192 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014 Vishvananda Ishaya.
|
||||
Copyright 2014 Docker, Inc.
|
||||
|
||||
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.
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
# netlink - netlink library for go #
|
||||
|
||||
[](https://travis-ci.org/vishvananda/netlink) [](https://godoc.org/github.com/vishvananda/netlink)
|
||||
|
||||
The netlink package provides a simple netlink library for go. Netlink
|
||||
is the interface a user-space program in linux uses to communicate with
|
||||
the kernel. It can be used to add and remove interfaces, set ip addresses
|
||||
and routes, and configure ipsec. Netlink communication requires elevated
|
||||
privileges, so in most cases this code needs to be run as root. Since
|
||||
low-level netlink messages are inscrutable at best, the library attempts
|
||||
to provide an api that is loosely modeled on the CLI provied by iproute2.
|
||||
Actions like `ip link add` will be accomplished via a similarly named
|
||||
function like AddLink(). This library began its life as a fork of the
|
||||
netlink functionality in
|
||||
[docker/libcontainer](https://github.com/docker/libcontainer) but was
|
||||
heavily rewritten to improve testability, performance, and to add new
|
||||
functionality like ipsec xfrm handling.
|
||||
|
||||
## Local Build and Test ##
|
||||
|
||||
You can use go get command:
|
||||
|
||||
go get github.com/vishvananda/netlink
|
||||
|
||||
Testing dependencies:
|
||||
|
||||
go get github.com/vishvananda/netns
|
||||
|
||||
Testing (requires root):
|
||||
|
||||
sudo -E go test github.com/vishvananda/netlink
|
||||
|
||||
## Examples ##
|
||||
|
||||
Add a new bridge and add eth1 into it:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func main() {
|
||||
la := netlink.NewLinkAttrs()
|
||||
la.Name = "foo"
|
||||
mybridge := &netlink.Bridge{la}}
|
||||
_ := netlink.LinkAdd(mybridge)
|
||||
eth1, _ := netlink.LinkByName("eth1")
|
||||
netlink.LinkSetMaster(eth1, mybridge)
|
||||
}
|
||||
|
||||
```
|
||||
Note `NewLinkAttrs` constructor, it sets default values in structure. For now
|
||||
it sets only `TxQLen` to `-1`, so kernel will set default by itself. If you're
|
||||
using simple initialization(`LinkAttrs{Name: "foo"}`) `TxQLen` will be set to
|
||||
`0` unless you specify it like `LinkAttrs{Name: "foo", TxQLen: 1000}`.
|
||||
|
||||
Add a new ip address to loopback:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func main() {
|
||||
lo, _ := netlink.LinkByName("lo")
|
||||
addr, _ := netlink.ParseAddr("169.254.169.254/32")
|
||||
netlink.AddrAdd(lo, addr)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Future Work ##
|
||||
|
||||
Many pieces of netlink are not yet fully supported in the high-level
|
||||
interface. Aspects of virtually all of the high-level objects don't exist.
|
||||
Many of the underlying primitives are there, so its a matter of putting
|
||||
the right fields into the high-level objects and making sure that they
|
||||
are serialized and deserialized correctly in the Add and List methods.
|
||||
|
||||
There are also a few pieces of low level netlink functionality that still
|
||||
need to be implemented. Routing rules are not in place and some of the
|
||||
more advanced link types. Hopefully there is decent structure and testing
|
||||
in place to make these fairly straightforward to add.
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Addr represents an IP address from netlink. Netlink ip addresses
|
||||
// include a mask, so it stores the address as a net.IPNet.
|
||||
type Addr struct {
|
||||
*net.IPNet
|
||||
Label string
|
||||
}
|
||||
|
||||
// String returns $ip/$netmask $label
|
||||
func (a Addr) String() string {
|
||||
return fmt.Sprintf("%s %s", a.IPNet, a.Label)
|
||||
}
|
||||
|
||||
// ParseAddr parses the string representation of an address in the
|
||||
// form $ip/$netmask $label. The label portion is optional
|
||||
func ParseAddr(s string) (*Addr, error) {
|
||||
label := ""
|
||||
parts := strings.Split(s, " ")
|
||||
if len(parts) > 1 {
|
||||
s = parts[0]
|
||||
label = parts[1]
|
||||
}
|
||||
m, err := ParseIPNet(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Addr{IPNet: m, Label: label}, nil
|
||||
}
|
||||
|
||||
// Equal returns true if both Addrs have the same net.IPNet value.
|
||||
func (a Addr) Equal(x Addr) bool {
|
||||
sizea, _ := a.Mask.Size()
|
||||
sizeb, _ := x.Mask.Size()
|
||||
// ignore label for comparison
|
||||
return a.IP.Equal(x.IP) && sizea == sizeb
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// AddrAdd will add an IP address to a link device.
|
||||
// Equivalent to: `ip addr add $addr dev $link`
|
||||
func AddrAdd(link Link, addr *Addr) error {
|
||||
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
return addrHandle(link, addr, req)
|
||||
}
|
||||
|
||||
// AddrDel will delete an IP address from a link device.
|
||||
// Equivalent to: `ip addr del $addr dev $link`
|
||||
func AddrDel(link Link, addr *Addr) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK)
|
||||
return addrHandle(link, addr, req)
|
||||
}
|
||||
|
||||
func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
|
||||
base := link.Attrs()
|
||||
if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
|
||||
return fmt.Errorf("label must begin with interface name")
|
||||
}
|
||||
ensureIndex(base)
|
||||
|
||||
family := nl.GetIPFamily(addr.IP)
|
||||
|
||||
msg := nl.NewIfAddrmsg(family)
|
||||
msg.Index = uint32(base.Index)
|
||||
prefixlen, _ := addr.Mask.Size()
|
||||
msg.Prefixlen = uint8(prefixlen)
|
||||
req.AddData(msg)
|
||||
|
||||
var addrData []byte
|
||||
if family == FAMILY_V4 {
|
||||
addrData = addr.IP.To4()
|
||||
} else {
|
||||
addrData = addr.IP.To16()
|
||||
}
|
||||
|
||||
localData := nl.NewRtAttr(syscall.IFA_LOCAL, addrData)
|
||||
req.AddData(localData)
|
||||
|
||||
addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData)
|
||||
req.AddData(addressData)
|
||||
|
||||
if addr.Label != "" {
|
||||
labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
|
||||
req.AddData(labelData)
|
||||
}
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddrList gets a list of IP addresses in the system.
|
||||
// Equivalent to: `ip addr show`.
|
||||
// The list can be filtered by link and ip family.
|
||||
func AddrList(link Link, family int) ([]Addr, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP)
|
||||
msg := nl.NewIfInfomsg(family)
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := 0
|
||||
if link != nil {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
index = base.Index
|
||||
}
|
||||
|
||||
var res []Addr
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeIfAddrmsg(m)
|
||||
|
||||
if link != nil && msg.Index != uint32(index) {
|
||||
// Ignore messages from other interfaces
|
||||
continue
|
||||
}
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var local, dst *net.IPNet
|
||||
var addr Addr
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case syscall.IFA_ADDRESS:
|
||||
dst = &net.IPNet{
|
||||
IP: attr.Value,
|
||||
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
|
||||
}
|
||||
case syscall.IFA_LOCAL:
|
||||
local = &net.IPNet{
|
||||
IP: attr.Value,
|
||||
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
|
||||
}
|
||||
case syscall.IFA_LABEL:
|
||||
addr.Label = string(attr.Value[:len(attr.Value)-1])
|
||||
}
|
||||
}
|
||||
|
||||
// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
|
||||
if local != nil {
|
||||
addr.IPNet = local
|
||||
} else {
|
||||
addr.IPNet = dst
|
||||
}
|
||||
|
||||
res = append(res, addr)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Filter interface {
|
||||
Attrs() *FilterAttrs
|
||||
Type() string
|
||||
}
|
||||
|
||||
// Filter represents a netlink filter. A filter is associated with a link,
|
||||
// has a handle and a parent. The root filter of a device should have a
|
||||
// parent == HANDLE_ROOT.
|
||||
type FilterAttrs struct {
|
||||
LinkIndex int
|
||||
Handle uint32
|
||||
Parent uint32
|
||||
Priority uint16 // lower is higher priority
|
||||
Protocol uint16 // syscall.ETH_P_*
|
||||
}
|
||||
|
||||
func (q FilterAttrs) String() string {
|
||||
return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Priority: %d, Protocol: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Priority, q.Protocol)
|
||||
}
|
||||
|
||||
// U32 filters on many packet related properties
|
||||
type U32 struct {
|
||||
FilterAttrs
|
||||
// Currently only supports redirecting to another interface
|
||||
RedirIndex int
|
||||
}
|
||||
|
||||
func (filter *U32) Attrs() *FilterAttrs {
|
||||
return &filter.FilterAttrs
|
||||
}
|
||||
|
||||
func (filter *U32) Type() string {
|
||||
return "u32"
|
||||
}
|
||||
|
||||
// GenericFilter filters represent types that are not currently understood
|
||||
// by this netlink library.
|
||||
type GenericFilter struct {
|
||||
FilterAttrs
|
||||
FilterType string
|
||||
}
|
||||
|
||||
func (filter *GenericFilter) Attrs() *FilterAttrs {
|
||||
return &filter.FilterAttrs
|
||||
}
|
||||
|
||||
func (filter *GenericFilter) Type() string {
|
||||
return filter.FilterType
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// FilterDel will delete a filter from the system.
|
||||
// Equivalent to: `tc filter del $filter`
|
||||
func FilterDel(filter Filter) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_DELTFILTER, syscall.NLM_F_ACK)
|
||||
base := filter.Attrs()
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: int32(base.LinkIndex),
|
||||
Handle: base.Handle,
|
||||
Parent: base.Parent,
|
||||
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
|
||||
}
|
||||
req.AddData(msg)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterAdd will add a filter to the system.
|
||||
// Equivalent to: `tc filter add $filter`
|
||||
func FilterAdd(filter Filter) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWTFILTER, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
base := filter.Attrs()
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: int32(base.LinkIndex),
|
||||
Handle: base.Handle,
|
||||
Parent: base.Parent,
|
||||
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
|
||||
}
|
||||
req.AddData(msg)
|
||||
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
|
||||
|
||||
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
|
||||
if u32, ok := filter.(*U32); ok {
|
||||
// match all
|
||||
sel := nl.TcU32Sel{
|
||||
Nkeys: 1,
|
||||
Flags: nl.TC_U32_TERMINAL,
|
||||
}
|
||||
sel.Keys = append(sel.Keys, nl.TcU32Key{})
|
||||
nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
|
||||
actions := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil)
|
||||
table := nl.NewRtAttrChild(actions, nl.TCA_ACT_TAB, nil)
|
||||
nl.NewRtAttrChild(table, nl.TCA_KIND, nl.ZeroTerminated("mirred"))
|
||||
// redirect to other interface
|
||||
mir := nl.TcMirred{
|
||||
Action: nl.TC_ACT_STOLEN,
|
||||
Eaction: nl.TCA_EGRESS_REDIR,
|
||||
Ifindex: uint32(u32.RedirIndex),
|
||||
}
|
||||
aopts := nl.NewRtAttrChild(table, nl.TCA_OPTIONS, nil)
|
||||
nl.NewRtAttrChild(aopts, nl.TCA_MIRRED_PARMS, mir.Serialize())
|
||||
}
|
||||
req.AddData(options)
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterList gets a list of filters in the system.
|
||||
// Equivalent to: `tc filter show`.
|
||||
// Generally retunrs nothing if link and parent are not specified.
|
||||
func FilterList(link Link, parent uint32) ([]Filter, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETTFILTER, syscall.NLM_F_DUMP)
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Parent: parent,
|
||||
}
|
||||
if link != nil {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
msg.Ifindex = int32(base.Index)
|
||||
}
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTFILTER)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []Filter
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeTcMsg(m)
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base := FilterAttrs{
|
||||
LinkIndex: int(msg.Ifindex),
|
||||
Handle: msg.Handle,
|
||||
Parent: msg.Parent,
|
||||
}
|
||||
base.Priority, base.Protocol = MajorMinor(msg.Info)
|
||||
base.Protocol = nl.Swap16(base.Protocol)
|
||||
|
||||
var filter Filter
|
||||
filterType := ""
|
||||
detailed := false
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case nl.TCA_KIND:
|
||||
filterType = string(attr.Value[:len(attr.Value)-1])
|
||||
switch filterType {
|
||||
case "u32":
|
||||
filter = &U32{}
|
||||
default:
|
||||
filter = &GenericFilter{FilterType: filterType}
|
||||
}
|
||||
case nl.TCA_OPTIONS:
|
||||
switch filterType {
|
||||
case "u32":
|
||||
data, err := nl.ParseRouteAttr(attr.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
detailed, err = parseU32Data(filter, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// only return the detailed version of the filter
|
||||
if detailed {
|
||||
*filter.Attrs() = base
|
||||
res = append(res, filter)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
|
||||
native = nl.NativeEndian()
|
||||
u32 := filter.(*U32)
|
||||
detailed := false
|
||||
for _, datum := range data {
|
||||
switch datum.Attr.Type {
|
||||
case nl.TCA_U32_SEL:
|
||||
detailed = true
|
||||
sel := nl.DeserializeTcU32Sel(datum.Value)
|
||||
// only parse if we have a very basic redirect
|
||||
if sel.Flags&nl.TC_U32_TERMINAL == 0 || sel.Nkeys != 1 {
|
||||
return detailed, nil
|
||||
}
|
||||
case nl.TCA_U32_ACT:
|
||||
table, err := nl.ParseRouteAttr(datum.Value)
|
||||
if err != nil {
|
||||
return detailed, err
|
||||
}
|
||||
if len(table) != 1 || table[0].Attr.Type != nl.TCA_ACT_TAB {
|
||||
return detailed, fmt.Errorf("Action table not formed properly")
|
||||
}
|
||||
aattrs, err := nl.ParseRouteAttr(table[0].Value)
|
||||
for _, aattr := range aattrs {
|
||||
switch aattr.Attr.Type {
|
||||
case nl.TCA_KIND:
|
||||
actionType := string(aattr.Value[:len(aattr.Value)-1])
|
||||
// only parse if the action is mirred
|
||||
if actionType != "mirred" {
|
||||
return detailed, nil
|
||||
}
|
||||
case nl.TCA_OPTIONS:
|
||||
adata, err := nl.ParseRouteAttr(aattr.Value)
|
||||
if err != nil {
|
||||
return detailed, err
|
||||
}
|
||||
for _, adatum := range adata {
|
||||
switch adatum.Attr.Type {
|
||||
case nl.TCA_MIRRED_PARMS:
|
||||
mir := nl.DeserializeTcMirred(adatum.Value)
|
||||
u32.RedirIndex = int(mir.Ifindex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return detailed, nil
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package netlink
|
||||
|
||||
import "net"
|
||||
|
||||
// Link represents a link device from netlink. Shared link attributes
|
||||
// like name may be retrieved using the Attrs() method. Unique data
|
||||
// can be retrieved by casting the object to the proper type.
|
||||
type Link interface {
|
||||
Attrs() *LinkAttrs
|
||||
Type() string
|
||||
}
|
||||
|
||||
type (
|
||||
NsPid int
|
||||
NsFd int
|
||||
)
|
||||
|
||||
// LinkAttrs represents data shared by most link types
|
||||
type LinkAttrs struct {
|
||||
Index int
|
||||
MTU int
|
||||
TxQLen int // Transmit Queue Length
|
||||
Name string
|
||||
HardwareAddr net.HardwareAddr
|
||||
Flags net.Flags
|
||||
ParentIndex int // index of the parent link device
|
||||
MasterIndex int // must be the index of a bridge
|
||||
Namespace interface{} // nil | NsPid | NsFd
|
||||
}
|
||||
|
||||
// NewLinkAttrs returns LinkAttrs structure filled with default values
|
||||
func NewLinkAttrs() LinkAttrs {
|
||||
return LinkAttrs{
|
||||
TxQLen: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Device links cannot be created via netlink. These links
|
||||
// are links created by udev like 'lo' and 'etho0'
|
||||
type Device struct {
|
||||
LinkAttrs
|
||||
}
|
||||
|
||||
func (device *Device) Attrs() *LinkAttrs {
|
||||
return &device.LinkAttrs
|
||||
}
|
||||
|
||||
func (device *Device) Type() string {
|
||||
return "device"
|
||||
}
|
||||
|
||||
// Dummy links are dummy ethernet devices
|
||||
type Dummy struct {
|
||||
LinkAttrs
|
||||
}
|
||||
|
||||
func (dummy *Dummy) Attrs() *LinkAttrs {
|
||||
return &dummy.LinkAttrs
|
||||
}
|
||||
|
||||
func (dummy *Dummy) Type() string {
|
||||
return "dummy"
|
||||
}
|
||||
|
||||
// Ifb links are advanced dummy devices for packet filtering
|
||||
type Ifb struct {
|
||||
LinkAttrs
|
||||
}
|
||||
|
||||
func (ifb *Ifb) Attrs() *LinkAttrs {
|
||||
return &ifb.LinkAttrs
|
||||
}
|
||||
|
||||
func (ifb *Ifb) Type() string {
|
||||
return "ifb"
|
||||
}
|
||||
|
||||
// Bridge links are simple linux bridges
|
||||
type Bridge struct {
|
||||
LinkAttrs
|
||||
}
|
||||
|
||||
func (bridge *Bridge) Attrs() *LinkAttrs {
|
||||
return &bridge.LinkAttrs
|
||||
}
|
||||
|
||||
func (bridge *Bridge) Type() string {
|
||||
return "bridge"
|
||||
}
|
||||
|
||||
// Vlan links have ParentIndex set in their Attrs()
|
||||
type Vlan struct {
|
||||
LinkAttrs
|
||||
VlanId int
|
||||
}
|
||||
|
||||
func (vlan *Vlan) Attrs() *LinkAttrs {
|
||||
return &vlan.LinkAttrs
|
||||
}
|
||||
|
||||
func (vlan *Vlan) Type() string {
|
||||
return "vlan"
|
||||
}
|
||||
|
||||
type MacvlanMode uint16
|
||||
|
||||
const (
|
||||
MACVLAN_MODE_DEFAULT MacvlanMode = iota
|
||||
MACVLAN_MODE_PRIVATE
|
||||
MACVLAN_MODE_VEPA
|
||||
MACVLAN_MODE_BRIDGE
|
||||
MACVLAN_MODE_PASSTHRU
|
||||
MACVLAN_MODE_SOURCE
|
||||
)
|
||||
|
||||
// Macvlan links have ParentIndex set in their Attrs()
|
||||
type Macvlan struct {
|
||||
LinkAttrs
|
||||
Mode MacvlanMode
|
||||
}
|
||||
|
||||
func (macvlan *Macvlan) Attrs() *LinkAttrs {
|
||||
return &macvlan.LinkAttrs
|
||||
}
|
||||
|
||||
func (macvlan *Macvlan) Type() string {
|
||||
return "macvlan"
|
||||
}
|
||||
|
||||
// Macvtap - macvtap is a virtual interfaces based on macvlan
|
||||
type Macvtap struct {
|
||||
Macvlan
|
||||
}
|
||||
|
||||
func (macvtap Macvtap) Type() string {
|
||||
return "macvtap"
|
||||
}
|
||||
|
||||
// Veth devices must specify PeerName on create
|
||||
type Veth struct {
|
||||
LinkAttrs
|
||||
PeerName string // veth on create only
|
||||
}
|
||||
|
||||
func (veth *Veth) Attrs() *LinkAttrs {
|
||||
return &veth.LinkAttrs
|
||||
}
|
||||
|
||||
func (veth *Veth) Type() string {
|
||||
return "veth"
|
||||
}
|
||||
|
||||
// GenericLink links represent types that are not currently understood
|
||||
// by this netlink library.
|
||||
type GenericLink struct {
|
||||
LinkAttrs
|
||||
LinkType string
|
||||
}
|
||||
|
||||
func (generic *GenericLink) Attrs() *LinkAttrs {
|
||||
return &generic.LinkAttrs
|
||||
}
|
||||
|
||||
func (generic *GenericLink) Type() string {
|
||||
return generic.LinkType
|
||||
}
|
||||
|
||||
type Vxlan struct {
|
||||
LinkAttrs
|
||||
VxlanId int
|
||||
VtepDevIndex int
|
||||
SrcAddr net.IP
|
||||
Group net.IP
|
||||
TTL int
|
||||
TOS int
|
||||
Learning bool
|
||||
Proxy bool
|
||||
RSC bool
|
||||
L2miss bool
|
||||
L3miss bool
|
||||
NoAge bool
|
||||
GBP bool
|
||||
Age int
|
||||
Limit int
|
||||
Port int
|
||||
PortLow int
|
||||
PortHigh int
|
||||
}
|
||||
|
||||
func (vxlan *Vxlan) Attrs() *LinkAttrs {
|
||||
return &vxlan.LinkAttrs
|
||||
}
|
||||
|
||||
func (vxlan *Vxlan) Type() string {
|
||||
return "vxlan"
|
||||
}
|
||||
|
||||
type IPVlanMode uint16
|
||||
|
||||
const (
|
||||
IPVLAN_MODE_L2 IPVlanMode = iota
|
||||
IPVLAN_MODE_L3
|
||||
IPVLAN_MODE_MAX
|
||||
)
|
||||
|
||||
type IPVlan struct {
|
||||
LinkAttrs
|
||||
Mode IPVlanMode
|
||||
}
|
||||
|
||||
func (ipvlan *IPVlan) Attrs() *LinkAttrs {
|
||||
return &ipvlan.LinkAttrs
|
||||
}
|
||||
|
||||
func (ipvlan *IPVlan) Type() string {
|
||||
return "ipvlan"
|
||||
}
|
||||
|
||||
// iproute2 supported devices;
|
||||
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
|
||||
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
|
||||
// gre | gretap | ip6gre | ip6gretap | vti | nlmon |
|
||||
// bond_slave | ipvlan
|
||||
+750
@@ -0,0 +1,750 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
var native = nl.NativeEndian()
|
||||
var lookupByDump = false
|
||||
|
||||
var macvlanModes = [...]uint32{
|
||||
0,
|
||||
nl.MACVLAN_MODE_PRIVATE,
|
||||
nl.MACVLAN_MODE_VEPA,
|
||||
nl.MACVLAN_MODE_BRIDGE,
|
||||
nl.MACVLAN_MODE_PASSTHRU,
|
||||
nl.MACVLAN_MODE_SOURCE,
|
||||
}
|
||||
|
||||
func ensureIndex(link *LinkAttrs) {
|
||||
if link != nil && link.Index == 0 {
|
||||
newlink, _ := LinkByName(link.Name)
|
||||
if newlink != nil {
|
||||
link.Index = newlink.Attrs().Index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LinkSetUp enables the link device.
|
||||
// Equivalent to: `ip link set $link up`
|
||||
func LinkSetUp(link Link) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Change = syscall.IFF_UP
|
||||
msg.Flags = syscall.IFF_UP
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetDown disables link device.
|
||||
// Equivalent to: `ip link set $link down`
|
||||
func LinkSetDown(link Link) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Change = syscall.IFF_UP
|
||||
msg.Flags = 0 & ^syscall.IFF_UP
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetMTU sets the mtu of the link device.
|
||||
// Equivalent to: `ip link set $link mtu $mtu`
|
||||
func LinkSetMTU(link Link, mtu int) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(mtu))
|
||||
|
||||
data := nl.NewRtAttr(syscall.IFLA_MTU, b)
|
||||
req.AddData(data)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetName sets the name of the link device.
|
||||
// Equivalent to: `ip link set $link name $name`
|
||||
func LinkSetName(link Link, name string) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name))
|
||||
req.AddData(data)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetHardwareAddr sets the hardware address of the link device.
|
||||
// Equivalent to: `ip link set $link address $hwaddr`
|
||||
func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr))
|
||||
req.AddData(data)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetMaster sets the master of the link device.
|
||||
// Equivalent to: `ip link set $link master $master`
|
||||
func LinkSetMaster(link Link, master *Bridge) error {
|
||||
index := 0
|
||||
if master != nil {
|
||||
masterBase := master.Attrs()
|
||||
ensureIndex(masterBase)
|
||||
index = masterBase.Index
|
||||
}
|
||||
return LinkSetMasterByIndex(link, index)
|
||||
}
|
||||
|
||||
// LinkSetMasterByIndex sets the master of the link device.
|
||||
// Equivalent to: `ip link set $link master $master`
|
||||
func LinkSetMasterByIndex(link Link, masterIndex int) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(masterIndex))
|
||||
|
||||
data := nl.NewRtAttr(syscall.IFLA_MASTER, b)
|
||||
req.AddData(data)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetNsPid puts the device into a new network namespace. The
|
||||
// pid must be a pid of a running process.
|
||||
// Equivalent to: `ip link set $link netns $pid`
|
||||
func LinkSetNsPid(link Link, nspid int) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(nspid))
|
||||
|
||||
data := nl.NewRtAttr(syscall.IFLA_NET_NS_PID, b)
|
||||
req.AddData(data)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetNsFd puts the device into a new network namespace. The
|
||||
// fd must be an open file descriptor to a network namespace.
|
||||
// Similar to: `ip link set $link netns $ns`
|
||||
func LinkSetNsFd(link Link, fd int) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(fd))
|
||||
|
||||
data := nl.NewRtAttr(nl.IFLA_NET_NS_FD, b)
|
||||
req.AddData(data)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func boolAttr(val bool) []byte {
|
||||
var v uint8
|
||||
if val {
|
||||
v = 1
|
||||
}
|
||||
return nl.Uint8Attr(v)
|
||||
}
|
||||
|
||||
type vxlanPortRange struct {
|
||||
Lo, Hi uint16
|
||||
}
|
||||
|
||||
func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_ID, nl.Uint32Attr(uint32(vxlan.VxlanId)))
|
||||
if vxlan.VtepDevIndex != 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LINK, nl.Uint32Attr(uint32(vxlan.VtepDevIndex)))
|
||||
}
|
||||
if vxlan.SrcAddr != nil {
|
||||
ip := vxlan.SrcAddr.To4()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL, []byte(ip))
|
||||
} else {
|
||||
ip = vxlan.SrcAddr.To16()
|
||||
if ip != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL6, []byte(ip))
|
||||
}
|
||||
}
|
||||
}
|
||||
if vxlan.Group != nil {
|
||||
group := vxlan.Group.To4()
|
||||
if group != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP, []byte(group))
|
||||
} else {
|
||||
group = vxlan.Group.To16()
|
||||
if group != nil {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP6, []byte(group))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TTL, nl.Uint8Attr(uint8(vxlan.TTL)))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TOS, nl.Uint8Attr(uint8(vxlan.TOS)))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LEARNING, boolAttr(vxlan.Learning))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PROXY, boolAttr(vxlan.Proxy))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_RSC, boolAttr(vxlan.RSC))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L2MISS, boolAttr(vxlan.L2miss))
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L3MISS, boolAttr(vxlan.L3miss))
|
||||
|
||||
if vxlan.GBP {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, boolAttr(vxlan.GBP))
|
||||
}
|
||||
|
||||
if vxlan.NoAge {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
|
||||
} else if vxlan.Age > 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(uint32(vxlan.Age)))
|
||||
}
|
||||
if vxlan.Limit > 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit)))
|
||||
}
|
||||
if vxlan.Port > 0 {
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT, nl.Uint16Attr(uint16(vxlan.Port)))
|
||||
}
|
||||
if vxlan.PortLow > 0 || vxlan.PortHigh > 0 {
|
||||
pr := vxlanPortRange{uint16(vxlan.PortLow), uint16(vxlan.PortHigh)}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, binary.BigEndian, &pr)
|
||||
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT_RANGE, buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// LinkAdd adds a new link device. The type and features of the device
|
||||
// are taken fromt the parameters in the link object.
|
||||
// Equivalent to: `ip link add $link`
|
||||
func LinkAdd(link Link) error {
|
||||
// TODO: set mtu and hardware address
|
||||
// TODO: support extra data for macvlan
|
||||
base := link.Attrs()
|
||||
|
||||
if base.Name == "" {
|
||||
return fmt.Errorf("LinkAttrs.Name cannot be empty!")
|
||||
}
|
||||
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
req.AddData(msg)
|
||||
|
||||
if base.ParentIndex != 0 {
|
||||
b := make([]byte, 4)
|
||||
native.PutUint32(b, uint32(base.ParentIndex))
|
||||
data := nl.NewRtAttr(syscall.IFLA_LINK, b)
|
||||
req.AddData(data)
|
||||
} else if link.Type() == "ipvlan" {
|
||||
return fmt.Errorf("Can't create ipvlan link without ParentIndex")
|
||||
}
|
||||
|
||||
nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(base.Name))
|
||||
req.AddData(nameData)
|
||||
|
||||
if base.MTU > 0 {
|
||||
mtu := nl.NewRtAttr(syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
|
||||
req.AddData(mtu)
|
||||
}
|
||||
|
||||
if base.TxQLen >= 0 {
|
||||
qlen := nl.NewRtAttr(syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
|
||||
req.AddData(qlen)
|
||||
}
|
||||
|
||||
if base.Namespace != nil {
|
||||
var attr *nl.RtAttr
|
||||
switch base.Namespace.(type) {
|
||||
case NsPid:
|
||||
val := nl.Uint32Attr(uint32(base.Namespace.(NsPid)))
|
||||
attr = nl.NewRtAttr(syscall.IFLA_NET_NS_PID, val)
|
||||
case NsFd:
|
||||
val := nl.Uint32Attr(uint32(base.Namespace.(NsFd)))
|
||||
attr = nl.NewRtAttr(nl.IFLA_NET_NS_FD, val)
|
||||
}
|
||||
|
||||
req.AddData(attr)
|
||||
}
|
||||
|
||||
linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||
nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type()))
|
||||
|
||||
if vlan, ok := link.(*Vlan); ok {
|
||||
b := make([]byte, 2)
|
||||
native.PutUint16(b, uint16(vlan.VlanId))
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_VLAN_ID, b)
|
||||
} else if veth, ok := link.(*Veth); ok {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil)
|
||||
nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC)
|
||||
nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName))
|
||||
if base.TxQLen >= 0 {
|
||||
nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
|
||||
}
|
||||
if base.MTU > 0 {
|
||||
nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
|
||||
}
|
||||
|
||||
} else if vxlan, ok := link.(*Vxlan); ok {
|
||||
addVxlanAttrs(vxlan, linkInfo)
|
||||
} else if ipv, ok := link.(*IPVlan); ok {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
|
||||
} else if macv, ok := link.(*Macvlan); ok {
|
||||
if macv.Mode != MACVLAN_MODE_DEFAULT {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode]))
|
||||
}
|
||||
}
|
||||
|
||||
req.AddData(linkInfo)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ensureIndex(base)
|
||||
|
||||
// can't set master during create, so set it afterwards
|
||||
if base.MasterIndex != 0 {
|
||||
// TODO: verify MasterIndex is actually a bridge?
|
||||
return LinkSetMasterByIndex(link, base.MasterIndex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LinkDel deletes link device. Either Index or Name must be set in
|
||||
// the link object for it to be deleted. The other values are ignored.
|
||||
// Equivalent to: `ip link del $link`
|
||||
func LinkDel(link Link) error {
|
||||
base := link.Attrs()
|
||||
|
||||
ensureIndex(base)
|
||||
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func linkByNameDump(name string) (Link, error) {
|
||||
links, err := LinkList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, link := range links {
|
||||
if link.Attrs().Name == name {
|
||||
return link, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Link %s not found", name)
|
||||
}
|
||||
|
||||
// LinkByName finds a link by name and returns a pointer to the object.
|
||||
func LinkByName(name string) (Link, error) {
|
||||
if lookupByDump {
|
||||
return linkByNameDump(name)
|
||||
}
|
||||
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
req.AddData(msg)
|
||||
|
||||
nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(name))
|
||||
req.AddData(nameData)
|
||||
|
||||
link, err := execGetLink(req)
|
||||
if err == syscall.EINVAL {
|
||||
// older kernels don't support looking up via IFLA_IFNAME
|
||||
// so fall back to dumping all links
|
||||
lookupByDump = true
|
||||
return linkByNameDump(name)
|
||||
}
|
||||
|
||||
return link, err
|
||||
}
|
||||
|
||||
// LinkByIndex finds a link by index and returns a pointer to the object.
|
||||
func LinkByIndex(index int) (Link, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Index = int32(index)
|
||||
req.AddData(msg)
|
||||
|
||||
return execGetLink(req)
|
||||
}
|
||||
|
||||
func execGetLink(req *nl.NetlinkRequest) (Link, error) {
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
if errno == syscall.ENODEV {
|
||||
return nil, fmt.Errorf("Link not found")
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(msgs) == 0:
|
||||
return nil, fmt.Errorf("Link not found")
|
||||
|
||||
case len(msgs) == 1:
|
||||
return linkDeserialize(msgs[0])
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("More than one link found")
|
||||
}
|
||||
}
|
||||
|
||||
// linkDeserialize deserializes a raw message received from netlink into
|
||||
// a link object.
|
||||
func linkDeserialize(m []byte) (Link, error) {
|
||||
msg := nl.DeserializeIfInfomsg(m)
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)}
|
||||
var link Link
|
||||
linkType := ""
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case syscall.IFLA_LINKINFO:
|
||||
infos, err := nl.ParseRouteAttr(attr.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, info := range infos {
|
||||
switch info.Attr.Type {
|
||||
case nl.IFLA_INFO_KIND:
|
||||
linkType = string(info.Value[:len(info.Value)-1])
|
||||
switch linkType {
|
||||
case "dummy":
|
||||
link = &Dummy{}
|
||||
case "ifb":
|
||||
link = &Ifb{}
|
||||
case "bridge":
|
||||
link = &Bridge{}
|
||||
case "vlan":
|
||||
link = &Vlan{}
|
||||
case "veth":
|
||||
link = &Veth{}
|
||||
case "vxlan":
|
||||
link = &Vxlan{}
|
||||
case "ipvlan":
|
||||
link = &IPVlan{}
|
||||
case "macvlan":
|
||||
link = &Macvlan{}
|
||||
case "macvtap":
|
||||
link = &Macvtap{}
|
||||
default:
|
||||
link = &GenericLink{LinkType: linkType}
|
||||
}
|
||||
case nl.IFLA_INFO_DATA:
|
||||
data, err := nl.ParseRouteAttr(info.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch linkType {
|
||||
case "vlan":
|
||||
parseVlanData(link, data)
|
||||
case "vxlan":
|
||||
parseVxlanData(link, data)
|
||||
case "ipvlan":
|
||||
parseIPVlanData(link, data)
|
||||
case "macvlan":
|
||||
parseMacvlanData(link, data)
|
||||
case "macvtap":
|
||||
parseMacvtapData(link, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
case syscall.IFLA_ADDRESS:
|
||||
var nonzero bool
|
||||
for _, b := range attr.Value {
|
||||
if b != 0 {
|
||||
nonzero = true
|
||||
}
|
||||
}
|
||||
if nonzero {
|
||||
base.HardwareAddr = attr.Value[:]
|
||||
}
|
||||
case syscall.IFLA_IFNAME:
|
||||
base.Name = string(attr.Value[:len(attr.Value)-1])
|
||||
case syscall.IFLA_MTU:
|
||||
base.MTU = int(native.Uint32(attr.Value[0:4]))
|
||||
case syscall.IFLA_LINK:
|
||||
base.ParentIndex = int(native.Uint32(attr.Value[0:4]))
|
||||
case syscall.IFLA_MASTER:
|
||||
base.MasterIndex = int(native.Uint32(attr.Value[0:4]))
|
||||
case syscall.IFLA_TXQLEN:
|
||||
base.TxQLen = int(native.Uint32(attr.Value[0:4]))
|
||||
}
|
||||
}
|
||||
// Links that don't have IFLA_INFO_KIND are hardware devices
|
||||
if link == nil {
|
||||
link = &Device{}
|
||||
}
|
||||
*link.Attrs() = base
|
||||
|
||||
return link, nil
|
||||
}
|
||||
|
||||
// LinkList gets a list of link devices.
|
||||
// Equivalent to: `ip link show`
|
||||
func LinkList() ([]Link, error) {
|
||||
// NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need
|
||||
// to get the message ourselves to parse link type.
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []Link
|
||||
for _, m := range msgs {
|
||||
link, err := linkDeserialize(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, link)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func LinkSetHairpin(link Link, mode bool) error {
|
||||
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE)
|
||||
}
|
||||
|
||||
func LinkSetGuard(link Link, mode bool) error {
|
||||
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_GUARD)
|
||||
}
|
||||
|
||||
func LinkSetFastLeave(link Link, mode bool) error {
|
||||
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_FAST_LEAVE)
|
||||
}
|
||||
|
||||
func LinkSetLearning(link Link, mode bool) error {
|
||||
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING)
|
||||
}
|
||||
|
||||
func LinkSetRootBlock(link Link, mode bool) error {
|
||||
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROTECT)
|
||||
}
|
||||
|
||||
func LinkSetFlood(link Link, mode bool) error {
|
||||
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD)
|
||||
}
|
||||
|
||||
func setProtinfoAttr(link Link, mode bool, attr int) error {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
|
||||
msg.Index = int32(base.Index)
|
||||
req.AddData(msg)
|
||||
|
||||
br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil)
|
||||
nl.NewRtAttrChild(br, attr, boolToByte(mode))
|
||||
req.AddData(br)
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
vlan := link.(*Vlan)
|
||||
for _, datum := range data {
|
||||
switch datum.Attr.Type {
|
||||
case nl.IFLA_VLAN_ID:
|
||||
vlan.VlanId = int(native.Uint16(datum.Value[0:2]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
vxlan := link.(*Vxlan)
|
||||
for _, datum := range data {
|
||||
switch datum.Attr.Type {
|
||||
case nl.IFLA_VXLAN_ID:
|
||||
vxlan.VxlanId = int(native.Uint32(datum.Value[0:4]))
|
||||
case nl.IFLA_VXLAN_LINK:
|
||||
vxlan.VtepDevIndex = int(native.Uint32(datum.Value[0:4]))
|
||||
case nl.IFLA_VXLAN_LOCAL:
|
||||
vxlan.SrcAddr = net.IP(datum.Value[0:4])
|
||||
case nl.IFLA_VXLAN_LOCAL6:
|
||||
vxlan.SrcAddr = net.IP(datum.Value[0:16])
|
||||
case nl.IFLA_VXLAN_GROUP:
|
||||
vxlan.Group = net.IP(datum.Value[0:4])
|
||||
case nl.IFLA_VXLAN_GROUP6:
|
||||
vxlan.Group = net.IP(datum.Value[0:16])
|
||||
case nl.IFLA_VXLAN_TTL:
|
||||
vxlan.TTL = int(datum.Value[0])
|
||||
case nl.IFLA_VXLAN_TOS:
|
||||
vxlan.TOS = int(datum.Value[0])
|
||||
case nl.IFLA_VXLAN_LEARNING:
|
||||
vxlan.Learning = int8(datum.Value[0]) != 0
|
||||
case nl.IFLA_VXLAN_PROXY:
|
||||
vxlan.Proxy = int8(datum.Value[0]) != 0
|
||||
case nl.IFLA_VXLAN_RSC:
|
||||
vxlan.RSC = int8(datum.Value[0]) != 0
|
||||
case nl.IFLA_VXLAN_L2MISS:
|
||||
vxlan.L2miss = int8(datum.Value[0]) != 0
|
||||
case nl.IFLA_VXLAN_L3MISS:
|
||||
vxlan.L3miss = int8(datum.Value[0]) != 0
|
||||
case nl.IFLA_VXLAN_GBP:
|
||||
vxlan.GBP = int8(datum.Value[0]) != 0
|
||||
case nl.IFLA_VXLAN_AGEING:
|
||||
vxlan.Age = int(native.Uint32(datum.Value[0:4]))
|
||||
vxlan.NoAge = vxlan.Age == 0
|
||||
case nl.IFLA_VXLAN_LIMIT:
|
||||
vxlan.Limit = int(native.Uint32(datum.Value[0:4]))
|
||||
case nl.IFLA_VXLAN_PORT:
|
||||
vxlan.Port = int(native.Uint16(datum.Value[0:2]))
|
||||
case nl.IFLA_VXLAN_PORT_RANGE:
|
||||
buf := bytes.NewBuffer(datum.Value[0:4])
|
||||
var pr vxlanPortRange
|
||||
if binary.Read(buf, binary.BigEndian, &pr) != nil {
|
||||
vxlan.PortLow = int(pr.Lo)
|
||||
vxlan.PortHigh = int(pr.Hi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
ipv := link.(*IPVlan)
|
||||
for _, datum := range data {
|
||||
if datum.Attr.Type == nl.IFLA_IPVLAN_MODE {
|
||||
ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4]))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseMacvtapData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
macv := link.(*Macvtap)
|
||||
parseMacvlanData(&macv.Macvlan, data)
|
||||
}
|
||||
|
||||
func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
macv := link.(*Macvlan)
|
||||
for _, datum := range data {
|
||||
if datum.Attr.Type == nl.IFLA_MACVLAN_MODE {
|
||||
switch native.Uint32(datum.Value[0:4]) {
|
||||
case nl.MACVLAN_MODE_PRIVATE:
|
||||
macv.Mode = MACVLAN_MODE_PRIVATE
|
||||
case nl.MACVLAN_MODE_VEPA:
|
||||
macv.Mode = MACVLAN_MODE_VEPA
|
||||
case nl.MACVLAN_MODE_BRIDGE:
|
||||
macv.Mode = MACVLAN_MODE_BRIDGE
|
||||
case nl.MACVLAN_MODE_PASSTHRU:
|
||||
macv.Mode = MACVLAN_MODE_PASSTHRU
|
||||
case nl.MACVLAN_MODE_SOURCE:
|
||||
macv.Mode = MACVLAN_MODE_SOURCE
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copied from pkg/net_linux.go
|
||||
func linkFlags(rawFlags uint32) net.Flags {
|
||||
var f net.Flags
|
||||
if rawFlags&syscall.IFF_UP != 0 {
|
||||
f |= net.FlagUp
|
||||
}
|
||||
if rawFlags&syscall.IFF_BROADCAST != 0 {
|
||||
f |= net.FlagBroadcast
|
||||
}
|
||||
if rawFlags&syscall.IFF_LOOPBACK != 0 {
|
||||
f |= net.FlagLoopback
|
||||
}
|
||||
if rawFlags&syscall.IFF_POINTOPOINT != 0 {
|
||||
f |= net.FlagPointToPoint
|
||||
}
|
||||
if rawFlags&syscall.IFF_MULTICAST != 0 {
|
||||
f |= net.FlagMulticast
|
||||
}
|
||||
return f
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Neigh represents a link layer neighbor from netlink.
|
||||
type Neigh struct {
|
||||
LinkIndex int
|
||||
Family int
|
||||
State int
|
||||
Type int
|
||||
Flags int
|
||||
IP net.IP
|
||||
HardwareAddr net.HardwareAddr
|
||||
}
|
||||
|
||||
// String returns $ip/$hwaddr $label
|
||||
func (neigh *Neigh) String() string {
|
||||
return fmt.Sprintf("%s %s", neigh.IP, neigh.HardwareAddr)
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
const (
|
||||
NDA_UNSPEC = iota
|
||||
NDA_DST
|
||||
NDA_LLADDR
|
||||
NDA_CACHEINFO
|
||||
NDA_PROBES
|
||||
NDA_VLAN
|
||||
NDA_PORT
|
||||
NDA_VNI
|
||||
NDA_IFINDEX
|
||||
NDA_MAX = NDA_IFINDEX
|
||||
)
|
||||
|
||||
// Neighbor Cache Entry States.
|
||||
const (
|
||||
NUD_NONE = 0x00
|
||||
NUD_INCOMPLETE = 0x01
|
||||
NUD_REACHABLE = 0x02
|
||||
NUD_STALE = 0x04
|
||||
NUD_DELAY = 0x08
|
||||
NUD_PROBE = 0x10
|
||||
NUD_FAILED = 0x20
|
||||
NUD_NOARP = 0x40
|
||||
NUD_PERMANENT = 0x80
|
||||
)
|
||||
|
||||
// Neighbor Flags
|
||||
const (
|
||||
NTF_USE = 0x01
|
||||
NTF_SELF = 0x02
|
||||
NTF_MASTER = 0x04
|
||||
NTF_PROXY = 0x08
|
||||
NTF_ROUTER = 0x80
|
||||
)
|
||||
|
||||
type Ndmsg struct {
|
||||
Family uint8
|
||||
Index uint32
|
||||
State uint16
|
||||
Flags uint8
|
||||
Type uint8
|
||||
}
|
||||
|
||||
func deserializeNdmsg(b []byte) *Ndmsg {
|
||||
var dummy Ndmsg
|
||||
return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
|
||||
}
|
||||
|
||||
func (msg *Ndmsg) Serialize() []byte {
|
||||
return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
func (msg *Ndmsg) Len() int {
|
||||
return int(unsafe.Sizeof(*msg))
|
||||
}
|
||||
|
||||
// NeighAdd will add an IP to MAC mapping to the ARP table
|
||||
// Equivalent to: `ip neigh add ....`
|
||||
func NeighAdd(neigh *Neigh) error {
|
||||
return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL)
|
||||
}
|
||||
|
||||
// NeighAdd will add or replace an IP to MAC mapping to the ARP table
|
||||
// Equivalent to: `ip neigh replace....`
|
||||
func NeighSet(neigh *Neigh) error {
|
||||
return neighAdd(neigh, syscall.NLM_F_CREATE)
|
||||
}
|
||||
|
||||
// NeighAppend will append an entry to FDB
|
||||
// Equivalent to: `bridge fdb append...`
|
||||
func NeighAppend(neigh *Neigh) error {
|
||||
return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_APPEND)
|
||||
}
|
||||
|
||||
func neighAdd(neigh *Neigh, mode int) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWNEIGH, mode|syscall.NLM_F_ACK)
|
||||
return neighHandle(neigh, req)
|
||||
}
|
||||
|
||||
// NeighDel will delete an IP address from a link device.
|
||||
// Equivalent to: `ip addr del $addr dev $link`
|
||||
func NeighDel(neigh *Neigh) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_DELNEIGH, syscall.NLM_F_ACK)
|
||||
return neighHandle(neigh, req)
|
||||
}
|
||||
|
||||
func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
|
||||
var family int
|
||||
if neigh.Family > 0 {
|
||||
family = neigh.Family
|
||||
} else {
|
||||
family = nl.GetIPFamily(neigh.IP)
|
||||
}
|
||||
|
||||
msg := Ndmsg{
|
||||
Family: uint8(family),
|
||||
Index: uint32(neigh.LinkIndex),
|
||||
State: uint16(neigh.State),
|
||||
Type: uint8(neigh.Type),
|
||||
Flags: uint8(neigh.Flags),
|
||||
}
|
||||
req.AddData(&msg)
|
||||
|
||||
ipData := neigh.IP.To4()
|
||||
if ipData == nil {
|
||||
ipData = neigh.IP.To16()
|
||||
}
|
||||
|
||||
dstData := nl.NewRtAttr(NDA_DST, ipData)
|
||||
req.AddData(dstData)
|
||||
|
||||
hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
|
||||
req.AddData(hwData)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// NeighList gets a list of IP-MAC mappings in the system (ARP table).
|
||||
// Equivalent to: `ip neighbor show`.
|
||||
// The list can be filtered by link and ip family.
|
||||
func NeighList(linkIndex, family int) ([]Neigh, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP)
|
||||
msg := Ndmsg{
|
||||
Family: uint8(family),
|
||||
}
|
||||
req.AddData(&msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWNEIGH)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []Neigh
|
||||
for _, m := range msgs {
|
||||
ndm := deserializeNdmsg(m)
|
||||
if linkIndex != 0 && int(ndm.Index) != linkIndex {
|
||||
// Ignore messages from other interfaces
|
||||
continue
|
||||
}
|
||||
|
||||
neigh, err := NeighDeserialize(m)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
res = append(res, *neigh)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NeighDeserialize(m []byte) (*Neigh, error) {
|
||||
msg := deserializeNdmsg(m)
|
||||
|
||||
neigh := Neigh{
|
||||
LinkIndex: int(msg.Index),
|
||||
Family: int(msg.Family),
|
||||
State: int(msg.State),
|
||||
Type: int(msg.Type),
|
||||
Flags: int(msg.Flags),
|
||||
}
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case NDA_DST:
|
||||
neigh.IP = net.IP(attr.Value)
|
||||
case NDA_LLADDR:
|
||||
neigh.HardwareAddr = net.HardwareAddr(attr.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return &neigh, nil
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Package netlink provides a simple library for netlink. Netlink is
|
||||
// the interface a user-space program in linux uses to communicate with
|
||||
// the kernel. It can be used to add and remove interfaces, set up ip
|
||||
// addresses and routes, and confiugre ipsec. Netlink communication
|
||||
// requires elevated privileges, so in most cases this code needs to
|
||||
// be run as root. The low level primitives for netlink are contained
|
||||
// in the nl subpackage. This package attempts to provide a high-level
|
||||
// interface that is loosly modeled on the iproute2 cli.
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
const (
|
||||
// Family type definitions
|
||||
FAMILY_ALL = nl.FAMILY_ALL
|
||||
FAMILY_V4 = nl.FAMILY_V4
|
||||
FAMILY_V6 = nl.FAMILY_V6
|
||||
)
|
||||
|
||||
// ParseIPNet parses a string in ip/net format and returns a net.IPNet.
|
||||
// This is valuable because addresses in netlink are often IPNets and
|
||||
// ParseCIDR returns an IPNet with the IP part set to the base IP of the
|
||||
// range.
|
||||
func ParseIPNet(s string) (*net.IPNet, error) {
|
||||
ip, ipNet, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil
|
||||
}
|
||||
|
||||
// NewIPNet generates an IPNet from an ip address using a netmask of 32.
|
||||
func NewIPNet(ip net.IP) *net.IPNet {
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// +build !linux
|
||||
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
)
|
||||
|
||||
func LinkSetUp(link *Link) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkSetDown(link *Link) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkSetMTU(link *Link, mtu int) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkSetMaster(link *Link, master *Link) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkSetNsPid(link *Link, nspid int) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkSetNsFd(link *Link, fd int) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkAdd(link *Link) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkDel(link *Link) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func SetHairpin(link Link, mode bool) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func SetGuard(link Link, mode bool) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func SetFastLeave(link Link, mode bool) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func SetLearning(link Link, mode bool) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func SetRootBlock(link Link, mode bool) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func SetFlood(link Link, mode bool) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func LinkList() ([]Link, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func AddrAdd(link *Link, addr *Addr) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func AddrDel(link *Link, addr *Addr) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func AddrList(link *Link, family int) ([]Addr, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func RouteAdd(route *Route) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func RouteDel(route *Route) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func RouteList(link *Link, family int) ([]Route, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func XfrmPolicyAdd(policy *XfrmPolicy) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func XfrmPolicyDel(policy *XfrmPolicy) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func XfrmStateAdd(policy *XfrmState) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func XfrmStateDel(policy *XfrmState) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func XfrmStateList(family int) ([]XfrmState, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func NeighAdd(neigh *Neigh) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func NeighSet(neigh *Neigh) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func NeighAppend(neigh *Neigh) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func NeighDel(neigh *Neigh) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func NeighList(linkIndex, family int) ([]Neigh, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func NeighDeserialize(m []byte) (*Ndmsg, *Neigh, error) {
|
||||
return nil, nil, ErrNotImplemented
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package nl
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type IfAddrmsg struct {
|
||||
syscall.IfAddrmsg
|
||||
}
|
||||
|
||||
func NewIfAddrmsg(family int) *IfAddrmsg {
|
||||
return &IfAddrmsg{
|
||||
IfAddrmsg: syscall.IfAddrmsg{
|
||||
Family: uint8(family),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// struct ifaddrmsg {
|
||||
// __u8 ifa_family;
|
||||
// __u8 ifa_prefixlen; /* The prefix length */
|
||||
// __u8 ifa_flags; /* Flags */
|
||||
// __u8 ifa_scope; /* Address scope */
|
||||
// __u32 ifa_index; /* Link index */
|
||||
// };
|
||||
|
||||
// type IfAddrmsg struct {
|
||||
// Family uint8
|
||||
// Prefixlen uint8
|
||||
// Flags uint8
|
||||
// Scope uint8
|
||||
// Index uint32
|
||||
// }
|
||||
// SizeofIfAddrmsg = 0x8
|
||||
|
||||
func DeserializeIfAddrmsg(b []byte) *IfAddrmsg {
|
||||
return (*IfAddrmsg)(unsafe.Pointer(&b[0:syscall.SizeofIfAddrmsg][0]))
|
||||
}
|
||||
|
||||
func (msg *IfAddrmsg) Serialize() []byte {
|
||||
return (*(*[syscall.SizeofIfAddrmsg]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
func (msg *IfAddrmsg) Len() int {
|
||||
return syscall.SizeofIfAddrmsg
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package nl
|
||||
|
||||
const (
|
||||
DEFAULT_CHANGE = 0xFFFFFFFF
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_INFO_UNSPEC = iota
|
||||
IFLA_INFO_KIND
|
||||
IFLA_INFO_DATA
|
||||
IFLA_INFO_XSTATS
|
||||
IFLA_INFO_MAX = IFLA_INFO_XSTATS
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_VLAN_UNSPEC = iota
|
||||
IFLA_VLAN_ID
|
||||
IFLA_VLAN_FLAGS
|
||||
IFLA_VLAN_EGRESS_QOS
|
||||
IFLA_VLAN_INGRESS_QOS
|
||||
IFLA_VLAN_PROTOCOL
|
||||
IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL
|
||||
)
|
||||
|
||||
const (
|
||||
VETH_INFO_UNSPEC = iota
|
||||
VETH_INFO_PEER
|
||||
VETH_INFO_MAX = VETH_INFO_PEER
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_VXLAN_UNSPEC = iota
|
||||
IFLA_VXLAN_ID
|
||||
IFLA_VXLAN_GROUP
|
||||
IFLA_VXLAN_LINK
|
||||
IFLA_VXLAN_LOCAL
|
||||
IFLA_VXLAN_TTL
|
||||
IFLA_VXLAN_TOS
|
||||
IFLA_VXLAN_LEARNING
|
||||
IFLA_VXLAN_AGEING
|
||||
IFLA_VXLAN_LIMIT
|
||||
IFLA_VXLAN_PORT_RANGE
|
||||
IFLA_VXLAN_PROXY
|
||||
IFLA_VXLAN_RSC
|
||||
IFLA_VXLAN_L2MISS
|
||||
IFLA_VXLAN_L3MISS
|
||||
IFLA_VXLAN_PORT
|
||||
IFLA_VXLAN_GROUP6
|
||||
IFLA_VXLAN_LOCAL6
|
||||
IFLA_VXLAN_UDP_CSUM
|
||||
IFLA_VXLAN_UDP_ZERO_CSUM6_TX
|
||||
IFLA_VXLAN_UDP_ZERO_CSUM6_RX
|
||||
IFLA_VXLAN_REMCSUM_TX
|
||||
IFLA_VXLAN_REMCSUM_RX
|
||||
IFLA_VXLAN_GBP
|
||||
IFLA_VXLAN_REMCSUM_NOPARTIAL
|
||||
IFLA_VXLAN_FLOWBASED
|
||||
IFLA_VXLAN_MAX = IFLA_VXLAN_FLOWBASED
|
||||
)
|
||||
|
||||
const (
|
||||
BRIDGE_MODE_UNSPEC = iota
|
||||
BRIDGE_MODE_HAIRPIN
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_BRPORT_UNSPEC = iota
|
||||
IFLA_BRPORT_STATE
|
||||
IFLA_BRPORT_PRIORITY
|
||||
IFLA_BRPORT_COST
|
||||
IFLA_BRPORT_MODE
|
||||
IFLA_BRPORT_GUARD
|
||||
IFLA_BRPORT_PROTECT
|
||||
IFLA_BRPORT_FAST_LEAVE
|
||||
IFLA_BRPORT_LEARNING
|
||||
IFLA_BRPORT_UNICAST_FLOOD
|
||||
IFLA_BRPORT_MAX = IFLA_BRPORT_UNICAST_FLOOD
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_IPVLAN_UNSPEC = iota
|
||||
IFLA_IPVLAN_MODE
|
||||
IFLA_IPVLAN_MAX = IFLA_IPVLAN_MODE
|
||||
)
|
||||
|
||||
const (
|
||||
// not defined in syscall
|
||||
IFLA_NET_NS_FD = 28
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_MACVLAN_UNSPEC = iota
|
||||
IFLA_MACVLAN_MODE
|
||||
IFLA_MACVLAN_FLAGS
|
||||
IFLA_MACVLAN_MAX = IFLA_MACVLAN_FLAGS
|
||||
)
|
||||
|
||||
const (
|
||||
MACVLAN_MODE_PRIVATE = 1
|
||||
MACVLAN_MODE_VEPA = 2
|
||||
MACVLAN_MODE_BRIDGE = 4
|
||||
MACVLAN_MODE_PASSTHRU = 8
|
||||
MACVLAN_MODE_SOURCE = 16
|
||||
)
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
// Package nl has low level primitives for making Netlink calls.
|
||||
package nl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Family type definitions
|
||||
FAMILY_ALL = syscall.AF_UNSPEC
|
||||
FAMILY_V4 = syscall.AF_INET
|
||||
FAMILY_V6 = syscall.AF_INET6
|
||||
)
|
||||
|
||||
var nextSeqNr uint32
|
||||
|
||||
// GetIPFamily returns the family type of a net.IP.
|
||||
func GetIPFamily(ip net.IP) int {
|
||||
if len(ip) <= net.IPv4len {
|
||||
return FAMILY_V4
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
return FAMILY_V4
|
||||
}
|
||||
return FAMILY_V6
|
||||
}
|
||||
|
||||
var nativeEndian binary.ByteOrder
|
||||
|
||||
// Get native endianness for the system
|
||||
func NativeEndian() binary.ByteOrder {
|
||||
if nativeEndian == nil {
|
||||
var x uint32 = 0x01020304
|
||||
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||
nativeEndian = binary.BigEndian
|
||||
} else {
|
||||
nativeEndian = binary.LittleEndian
|
||||
}
|
||||
}
|
||||
return nativeEndian
|
||||
}
|
||||
|
||||
// Byte swap a 16 bit value if we aren't big endian
|
||||
func Swap16(i uint16) uint16 {
|
||||
if NativeEndian() == binary.BigEndian {
|
||||
return i
|
||||
}
|
||||
return (i&0xff00)>>8 | (i&0xff)<<8
|
||||
}
|
||||
|
||||
// Byte swap a 32 bit value if aren't big endian
|
||||
func Swap32(i uint32) uint32 {
|
||||
if NativeEndian() == binary.BigEndian {
|
||||
return i
|
||||
}
|
||||
return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24
|
||||
}
|
||||
|
||||
type NetlinkRequestData interface {
|
||||
Len() int
|
||||
Serialize() []byte
|
||||
}
|
||||
|
||||
// IfInfomsg is related to links, but it is used for list requests as well
|
||||
type IfInfomsg struct {
|
||||
syscall.IfInfomsg
|
||||
}
|
||||
|
||||
// Create an IfInfomsg with family specified
|
||||
func NewIfInfomsg(family int) *IfInfomsg {
|
||||
return &IfInfomsg{
|
||||
IfInfomsg: syscall.IfInfomsg{
|
||||
Family: uint8(family),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DeserializeIfInfomsg(b []byte) *IfInfomsg {
|
||||
return (*IfInfomsg)(unsafe.Pointer(&b[0:syscall.SizeofIfInfomsg][0]))
|
||||
}
|
||||
|
||||
func (msg *IfInfomsg) Serialize() []byte {
|
||||
return (*(*[syscall.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
func (msg *IfInfomsg) Len() int {
|
||||
return syscall.SizeofIfInfomsg
|
||||
}
|
||||
|
||||
func rtaAlignOf(attrlen int) int {
|
||||
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
|
||||
}
|
||||
|
||||
func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
|
||||
msg := NewIfInfomsg(family)
|
||||
parent.children = append(parent.children, msg)
|
||||
return msg
|
||||
}
|
||||
|
||||
// Extend RtAttr to handle data and children
|
||||
type RtAttr struct {
|
||||
syscall.RtAttr
|
||||
Data []byte
|
||||
children []NetlinkRequestData
|
||||
}
|
||||
|
||||
// Create a new Extended RtAttr object
|
||||
func NewRtAttr(attrType int, data []byte) *RtAttr {
|
||||
return &RtAttr{
|
||||
RtAttr: syscall.RtAttr{
|
||||
Type: uint16(attrType),
|
||||
},
|
||||
children: []NetlinkRequestData{},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new RtAttr obj anc add it as a child of an existing object
|
||||
func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
|
||||
attr := NewRtAttr(attrType, data)
|
||||
parent.children = append(parent.children, attr)
|
||||
return attr
|
||||
}
|
||||
|
||||
func (a *RtAttr) Len() int {
|
||||
if len(a.children) == 0 {
|
||||
return (syscall.SizeofRtAttr + len(a.Data))
|
||||
}
|
||||
|
||||
l := 0
|
||||
for _, child := range a.children {
|
||||
l += rtaAlignOf(child.Len())
|
||||
}
|
||||
l += syscall.SizeofRtAttr
|
||||
return rtaAlignOf(l + len(a.Data))
|
||||
}
|
||||
|
||||
// Serialize the RtAttr into a byte array
|
||||
// This can't just unsafe.cast because it must iterate through children.
|
||||
func (a *RtAttr) Serialize() []byte {
|
||||
native := NativeEndian()
|
||||
|
||||
length := a.Len()
|
||||
buf := make([]byte, rtaAlignOf(length))
|
||||
|
||||
if a.Data != nil {
|
||||
copy(buf[4:], a.Data)
|
||||
} else {
|
||||
next := 4
|
||||
for _, child := range a.children {
|
||||
childBuf := child.Serialize()
|
||||
copy(buf[next:], childBuf)
|
||||
next += rtaAlignOf(len(childBuf))
|
||||
}
|
||||
}
|
||||
|
||||
if l := uint16(length); l != 0 {
|
||||
native.PutUint16(buf[0:2], l)
|
||||
}
|
||||
native.PutUint16(buf[2:4], a.Type)
|
||||
return buf
|
||||
}
|
||||
|
||||
type NetlinkRequest struct {
|
||||
syscall.NlMsghdr
|
||||
Data []NetlinkRequestData
|
||||
}
|
||||
|
||||
// Serialize the Netlink Request into a byte array
|
||||
func (req *NetlinkRequest) Serialize() []byte {
|
||||
length := syscall.SizeofNlMsghdr
|
||||
dataBytes := make([][]byte, len(req.Data))
|
||||
for i, data := range req.Data {
|
||||
dataBytes[i] = data.Serialize()
|
||||
length = length + len(dataBytes[i])
|
||||
}
|
||||
req.Len = uint32(length)
|
||||
b := make([]byte, length)
|
||||
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
|
||||
next := syscall.SizeofNlMsghdr
|
||||
copy(b[0:next], hdr)
|
||||
for _, data := range dataBytes {
|
||||
for _, dataByte := range data {
|
||||
b[next] = dataByte
|
||||
next = next + 1
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
|
||||
if data != nil {
|
||||
req.Data = append(req.Data, data)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the request against a the given sockType.
|
||||
// Returns a list of netlink messages in seriaized format, optionally filtered
|
||||
// by resType.
|
||||
func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) {
|
||||
s, err := getNetlinkSocket(sockType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
if err := s.Send(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pid, err := s.GetPid()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res [][]byte
|
||||
|
||||
done:
|
||||
for {
|
||||
msgs, err := s.Receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if m.Header.Seq != req.Seq {
|
||||
return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
|
||||
}
|
||||
if m.Header.Pid != pid {
|
||||
return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_DONE {
|
||||
break done
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_ERROR {
|
||||
native := NativeEndian()
|
||||
error := int32(native.Uint32(m.Data[0:4]))
|
||||
if error == 0 {
|
||||
break done
|
||||
}
|
||||
return nil, syscall.Errno(-error)
|
||||
}
|
||||
if resType != 0 && m.Header.Type != resType {
|
||||
continue
|
||||
}
|
||||
res = append(res, m.Data)
|
||||
if m.Header.Flags&syscall.NLM_F_MULTI == 0 {
|
||||
break done
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Create a new netlink request from proto and flags
|
||||
// Note the Len value will be inaccurate once data is added until
|
||||
// the message is serialized
|
||||
func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
|
||||
return &NetlinkRequest{
|
||||
NlMsghdr: syscall.NlMsghdr{
|
||||
Len: uint32(syscall.SizeofNlMsghdr),
|
||||
Type: uint16(proto),
|
||||
Flags: syscall.NLM_F_REQUEST | uint16(flags),
|
||||
Seq: atomic.AddUint32(&nextSeqNr, 1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type NetlinkSocket struct {
|
||||
fd int
|
||||
lsa syscall.SockaddrNetlink
|
||||
}
|
||||
|
||||
func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
|
||||
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &NetlinkSocket{
|
||||
fd: fd,
|
||||
}
|
||||
s.lsa.Family = syscall.AF_NETLINK
|
||||
if err := syscall.Bind(fd, &s.lsa); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
|
||||
// and subscribe it to multicast groups passed in variable argument list.
|
||||
// Returns the netlink socket on which Receive() method can be called
|
||||
// to retrieve the messages from the kernel.
|
||||
func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
|
||||
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &NetlinkSocket{
|
||||
fd: fd,
|
||||
}
|
||||
s.lsa.Family = syscall.AF_NETLINK
|
||||
|
||||
for _, g := range groups {
|
||||
s.lsa.Groups |= (1 << (g - 1))
|
||||
}
|
||||
|
||||
if err := syscall.Bind(fd, &s.lsa); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) Close() {
|
||||
syscall.Close(s.fd)
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
|
||||
if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
|
||||
rb := make([]byte, syscall.Getpagesize())
|
||||
nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nr < syscall.NLMSG_HDRLEN {
|
||||
return nil, fmt.Errorf("Got short response from netlink")
|
||||
}
|
||||
rb = rb[:nr]
|
||||
return syscall.ParseNetlinkMessage(rb)
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) GetPid() (uint32, error) {
|
||||
lsa, err := syscall.Getsockname(s.fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch v := lsa.(type) {
|
||||
case *syscall.SockaddrNetlink:
|
||||
return v.Pid, nil
|
||||
}
|
||||
return 0, fmt.Errorf("Wrong socket type")
|
||||
}
|
||||
|
||||
func ZeroTerminated(s string) []byte {
|
||||
bytes := make([]byte, len(s)+1)
|
||||
for i := 0; i < len(s); i++ {
|
||||
bytes[i] = s[i]
|
||||
}
|
||||
bytes[len(s)] = 0
|
||||
return bytes
|
||||
}
|
||||
|
||||
func NonZeroTerminated(s string) []byte {
|
||||
bytes := make([]byte, len(s))
|
||||
for i := 0; i < len(s); i++ {
|
||||
bytes[i] = s[i]
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
func BytesToString(b []byte) string {
|
||||
n := bytes.Index(b, []byte{0})
|
||||
return string(b[:n])
|
||||
}
|
||||
|
||||
func Uint8Attr(v uint8) []byte {
|
||||
return []byte{byte(v)}
|
||||
}
|
||||
|
||||
func Uint16Attr(v uint16) []byte {
|
||||
native := NativeEndian()
|
||||
bytes := make([]byte, 2)
|
||||
native.PutUint16(bytes, v)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func Uint32Attr(v uint32) []byte {
|
||||
native := NativeEndian()
|
||||
bytes := make([]byte, 4)
|
||||
native.PutUint32(bytes, v)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) {
|
||||
var attrs []syscall.NetlinkRouteAttr
|
||||
for len(b) >= syscall.SizeofRtAttr {
|
||||
a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ra := syscall.NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-syscall.SizeofRtAttr]}
|
||||
attrs = append(attrs, ra)
|
||||
b = b[alen:]
|
||||
}
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func netlinkRouteAttrAndValue(b []byte) (*syscall.RtAttr, []byte, int, error) {
|
||||
a := (*syscall.RtAttr)(unsafe.Pointer(&b[0]))
|
||||
if int(a.Len) < syscall.SizeofRtAttr || int(a.Len) > len(b) {
|
||||
return nil, nil, 0, syscall.EINVAL
|
||||
}
|
||||
return a, b[syscall.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package nl
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type RtMsg struct {
|
||||
syscall.RtMsg
|
||||
}
|
||||
|
||||
func NewRtMsg() *RtMsg {
|
||||
return &RtMsg{
|
||||
RtMsg: syscall.RtMsg{
|
||||
Table: syscall.RT_TABLE_MAIN,
|
||||
Scope: syscall.RT_SCOPE_UNIVERSE,
|
||||
Protocol: syscall.RTPROT_BOOT,
|
||||
Type: syscall.RTN_UNICAST,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewRtDelMsg() *RtMsg {
|
||||
return &RtMsg{
|
||||
RtMsg: syscall.RtMsg{
|
||||
Table: syscall.RT_TABLE_MAIN,
|
||||
Scope: syscall.RT_SCOPE_NOWHERE,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *RtMsg) Len() int {
|
||||
return syscall.SizeofRtMsg
|
||||
}
|
||||
|
||||
func DeserializeRtMsg(b []byte) *RtMsg {
|
||||
return (*RtMsg)(unsafe.Pointer(&b[0:syscall.SizeofRtMsg][0]))
|
||||
}
|
||||
|
||||
func (msg *RtMsg) Serialize() []byte {
|
||||
return (*(*[syscall.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
package nl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Message types
|
||||
const (
|
||||
TCA_UNSPEC = iota
|
||||
TCA_KIND
|
||||
TCA_OPTIONS
|
||||
TCA_STATS
|
||||
TCA_XSTATS
|
||||
TCA_RATE
|
||||
TCA_FCNT
|
||||
TCA_STATS2
|
||||
TCA_STAB
|
||||
TCA_MAX = TCA_STAB
|
||||
)
|
||||
|
||||
const (
|
||||
TCA_ACT_TAB = 1
|
||||
TCAA_MAX = 1
|
||||
)
|
||||
|
||||
const (
|
||||
TCA_PRIO_UNSPEC = iota
|
||||
TCA_PRIO_MQ
|
||||
TCA_PRIO_MAX = TCA_PRIO_MQ
|
||||
)
|
||||
|
||||
const (
|
||||
SizeofTcMsg = 0x14
|
||||
SizeofTcActionMsg = 0x04
|
||||
SizeofTcPrioMap = 0x14
|
||||
SizeofTcRateSpec = 0x0c
|
||||
SizeofTcTbfQopt = 2*SizeofTcRateSpec + 0x0c
|
||||
SizeofTcU32Key = 0x10
|
||||
SizeofTcU32Sel = 0x10 // without keys
|
||||
SizeofTcMirred = 0x1c
|
||||
)
|
||||
|
||||
// struct tcmsg {
|
||||
// unsigned char tcm_family;
|
||||
// unsigned char tcm__pad1;
|
||||
// unsigned short tcm__pad2;
|
||||
// int tcm_ifindex;
|
||||
// __u32 tcm_handle;
|
||||
// __u32 tcm_parent;
|
||||
// __u32 tcm_info;
|
||||
// };
|
||||
|
||||
type TcMsg struct {
|
||||
Family uint8
|
||||
Pad [3]byte
|
||||
Ifindex int32
|
||||
Handle uint32
|
||||
Parent uint32
|
||||
Info uint32
|
||||
}
|
||||
|
||||
func (msg *TcMsg) Len() int {
|
||||
return SizeofTcMsg
|
||||
}
|
||||
|
||||
func DeserializeTcMsg(b []byte) *TcMsg {
|
||||
return (*TcMsg)(unsafe.Pointer(&b[0:SizeofTcMsg][0]))
|
||||
}
|
||||
|
||||
func (x *TcMsg) Serialize() []byte {
|
||||
return (*(*[SizeofTcMsg]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
// struct tcamsg {
|
||||
// unsigned char tca_family;
|
||||
// unsigned char tca__pad1;
|
||||
// unsigned short tca__pad2;
|
||||
// };
|
||||
|
||||
type TcActionMsg struct {
|
||||
Family uint8
|
||||
Pad [3]byte
|
||||
}
|
||||
|
||||
func (msg *TcActionMsg) Len() int {
|
||||
return SizeofTcActionMsg
|
||||
}
|
||||
|
||||
func DeserializeTcActionMsg(b []byte) *TcActionMsg {
|
||||
return (*TcActionMsg)(unsafe.Pointer(&b[0:SizeofTcActionMsg][0]))
|
||||
}
|
||||
|
||||
func (x *TcActionMsg) Serialize() []byte {
|
||||
return (*(*[SizeofTcActionMsg]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
const (
|
||||
TC_PRIO_MAX = 15
|
||||
)
|
||||
|
||||
// struct tc_prio_qopt {
|
||||
// int bands; /* Number of bands */
|
||||
// __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
|
||||
// };
|
||||
|
||||
type TcPrioMap struct {
|
||||
Bands int32
|
||||
Priomap [TC_PRIO_MAX + 1]uint8
|
||||
}
|
||||
|
||||
func (msg *TcPrioMap) Len() int {
|
||||
return SizeofTcPrioMap
|
||||
}
|
||||
|
||||
func DeserializeTcPrioMap(b []byte) *TcPrioMap {
|
||||
return (*TcPrioMap)(unsafe.Pointer(&b[0:SizeofTcPrioMap][0]))
|
||||
}
|
||||
|
||||
func (x *TcPrioMap) Serialize() []byte {
|
||||
return (*(*[SizeofTcPrioMap]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
const (
|
||||
TCA_TBF_UNSPEC = iota
|
||||
TCA_TBF_PARMS
|
||||
TCA_TBF_RTAB
|
||||
TCA_TBF_PTAB
|
||||
TCA_TBF_RATE64
|
||||
TCA_TBF_PRATE64
|
||||
TCA_TBF_BURST
|
||||
TCA_TBF_PBURST
|
||||
TCA_TBF_MAX = TCA_TBF_PBURST
|
||||
)
|
||||
|
||||
// struct tc_ratespec {
|
||||
// unsigned char cell_log;
|
||||
// __u8 linklayer; /* lower 4 bits */
|
||||
// unsigned short overhead;
|
||||
// short cell_align;
|
||||
// unsigned short mpu;
|
||||
// __u32 rate;
|
||||
// };
|
||||
|
||||
type TcRateSpec struct {
|
||||
CellLog uint8
|
||||
Linklayer uint8
|
||||
Overhead uint16
|
||||
CellAlign int16
|
||||
Mpu uint16
|
||||
Rate uint32
|
||||
}
|
||||
|
||||
func (msg *TcRateSpec) Len() int {
|
||||
return SizeofTcRateSpec
|
||||
}
|
||||
|
||||
func DeserializeTcRateSpec(b []byte) *TcRateSpec {
|
||||
return (*TcRateSpec)(unsafe.Pointer(&b[0:SizeofTcRateSpec][0]))
|
||||
}
|
||||
|
||||
func (x *TcRateSpec) Serialize() []byte {
|
||||
return (*(*[SizeofTcRateSpec]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
// struct tc_tbf_qopt {
|
||||
// struct tc_ratespec rate;
|
||||
// struct tc_ratespec peakrate;
|
||||
// __u32 limit;
|
||||
// __u32 buffer;
|
||||
// __u32 mtu;
|
||||
// };
|
||||
|
||||
type TcTbfQopt struct {
|
||||
Rate TcRateSpec
|
||||
Peakrate TcRateSpec
|
||||
Limit uint32
|
||||
Buffer uint32
|
||||
Mtu uint32
|
||||
}
|
||||
|
||||
func (msg *TcTbfQopt) Len() int {
|
||||
return SizeofTcTbfQopt
|
||||
}
|
||||
|
||||
func DeserializeTcTbfQopt(b []byte) *TcTbfQopt {
|
||||
return (*TcTbfQopt)(unsafe.Pointer(&b[0:SizeofTcTbfQopt][0]))
|
||||
}
|
||||
|
||||
func (x *TcTbfQopt) Serialize() []byte {
|
||||
return (*(*[SizeofTcTbfQopt]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
const (
|
||||
TCA_U32_UNSPEC = iota
|
||||
TCA_U32_CLASSID
|
||||
TCA_U32_HASH
|
||||
TCA_U32_LINK
|
||||
TCA_U32_DIVISOR
|
||||
TCA_U32_SEL
|
||||
TCA_U32_POLICE
|
||||
TCA_U32_ACT
|
||||
TCA_U32_INDEV
|
||||
TCA_U32_PCNT
|
||||
TCA_U32_MARK
|
||||
TCA_U32_MAX = TCA_U32_MARK
|
||||
)
|
||||
|
||||
// struct tc_u32_key {
|
||||
// __be32 mask;
|
||||
// __be32 val;
|
||||
// int off;
|
||||
// int offmask;
|
||||
// };
|
||||
|
||||
type TcU32Key struct {
|
||||
Mask uint32 // big endian
|
||||
Val uint32 // big endian
|
||||
Off int32
|
||||
OffMask int32
|
||||
}
|
||||
|
||||
func (msg *TcU32Key) Len() int {
|
||||
return SizeofTcU32Key
|
||||
}
|
||||
|
||||
func DeserializeTcU32Key(b []byte) *TcU32Key {
|
||||
return (*TcU32Key)(unsafe.Pointer(&b[0:SizeofTcU32Key][0]))
|
||||
}
|
||||
|
||||
func (x *TcU32Key) Serialize() []byte {
|
||||
return (*(*[SizeofTcU32Key]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
// struct tc_u32_sel {
|
||||
// unsigned char flags;
|
||||
// unsigned char offshift;
|
||||
// unsigned char nkeys;
|
||||
//
|
||||
// __be16 offmask;
|
||||
// __u16 off;
|
||||
// short offoff;
|
||||
//
|
||||
// short hoff;
|
||||
// __be32 hmask;
|
||||
// struct tc_u32_key keys[0];
|
||||
// };
|
||||
|
||||
const (
|
||||
TC_U32_TERMINAL = 1 << iota
|
||||
TC_U32_OFFSET = 1 << iota
|
||||
TC_U32_VAROFFSET = 1 << iota
|
||||
TC_U32_EAT = 1 << iota
|
||||
)
|
||||
|
||||
type TcU32Sel struct {
|
||||
Flags uint8
|
||||
Offshift uint8
|
||||
Nkeys uint8
|
||||
Pad uint8
|
||||
Offmask uint16 // big endian
|
||||
Off uint16
|
||||
Offoff int16
|
||||
Hoff int16
|
||||
Hmask uint32 // big endian
|
||||
Keys []TcU32Key
|
||||
}
|
||||
|
||||
func (msg *TcU32Sel) Len() int {
|
||||
return SizeofTcU32Sel + int(msg.Nkeys)*SizeofTcU32Key
|
||||
}
|
||||
|
||||
func DeserializeTcU32Sel(b []byte) *TcU32Sel {
|
||||
x := &TcU32Sel{}
|
||||
copy((*(*[SizeofTcU32Sel]byte)(unsafe.Pointer(x)))[:], b)
|
||||
next := SizeofTcU32Sel
|
||||
var i uint8
|
||||
for i = 0; i < x.Nkeys; i++ {
|
||||
x.Keys = append(x.Keys, *DeserializeTcU32Key(b[next:]))
|
||||
next += SizeofTcU32Key
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *TcU32Sel) Serialize() []byte {
|
||||
// This can't just unsafe.cast because it must iterate through keys.
|
||||
buf := make([]byte, x.Len())
|
||||
copy(buf, (*(*[SizeofTcU32Sel]byte)(unsafe.Pointer(x)))[:])
|
||||
next := SizeofTcU32Sel
|
||||
for _, key := range x.Keys {
|
||||
keyBuf := key.Serialize()
|
||||
copy(buf[next:], keyBuf)
|
||||
next += SizeofTcU32Key
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
const (
|
||||
TCA_ACT_MIRRED = 8
|
||||
)
|
||||
|
||||
const (
|
||||
TCA_MIRRED_UNSPEC = iota
|
||||
TCA_MIRRED_TM
|
||||
TCA_MIRRED_PARMS
|
||||
TCA_MIRRED_MAX = TCA_MIRRED_PARMS
|
||||
)
|
||||
|
||||
const (
|
||||
TCA_EGRESS_REDIR = 1 /* packet redirect to EGRESS*/
|
||||
TCA_EGRESS_MIRROR = 2 /* mirror packet to EGRESS */
|
||||
TCA_INGRESS_REDIR = 3 /* packet redirect to INGRESS*/
|
||||
TCA_INGRESS_MIRROR = 4 /* mirror packet to INGRESS */
|
||||
)
|
||||
|
||||
const (
|
||||
TC_ACT_UNSPEC = int32(-1)
|
||||
TC_ACT_OK = 0
|
||||
TC_ACT_RECLASSIFY = 1
|
||||
TC_ACT_SHOT = 2
|
||||
TC_ACT_PIPE = 3
|
||||
TC_ACT_STOLEN = 4
|
||||
TC_ACT_QUEUED = 5
|
||||
TC_ACT_REPEAT = 6
|
||||
TC_ACT_JUMP = 0x10000000
|
||||
)
|
||||
|
||||
// #define tc_gen \
|
||||
// __u32 index; \
|
||||
// __u32 capab; \
|
||||
// int action; \
|
||||
// int refcnt; \
|
||||
// int bindcnt
|
||||
// struct tc_mirred {
|
||||
// tc_gen;
|
||||
// int eaction; /* one of IN/EGRESS_MIRROR/REDIR */
|
||||
// __u32 ifindex; /* ifindex of egress port */
|
||||
// };
|
||||
|
||||
type TcMirred struct {
|
||||
Index uint32
|
||||
Capab uint32
|
||||
Action int32
|
||||
Refcnt int32
|
||||
Bindcnt int32
|
||||
Eaction int32
|
||||
Ifindex uint32
|
||||
}
|
||||
|
||||
func (msg *TcMirred) Len() int {
|
||||
return SizeofTcMirred
|
||||
}
|
||||
|
||||
func DeserializeTcMirred(b []byte) *TcMirred {
|
||||
return (*TcMirred)(unsafe.Pointer(&b[0:SizeofTcMirred][0]))
|
||||
}
|
||||
|
||||
func (x *TcMirred) Serialize() []byte {
|
||||
return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
package nl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Infinity for packet and byte counts
|
||||
const (
|
||||
XFRM_INF = ^uint64(0)
|
||||
)
|
||||
|
||||
// Message Types
|
||||
const (
|
||||
XFRM_MSG_BASE = 0x10
|
||||
XFRM_MSG_NEWSA = 0x10
|
||||
XFRM_MSG_DELSA = 0x11
|
||||
XFRM_MSG_GETSA = 0x12
|
||||
XFRM_MSG_NEWPOLICY = 0x13
|
||||
XFRM_MSG_DELPOLICY = 0x14
|
||||
XFRM_MSG_GETPOLICY = 0x15
|
||||
XFRM_MSG_ALLOCSPI = 0x16
|
||||
XFRM_MSG_ACQUIRE = 0x17
|
||||
XFRM_MSG_EXPIRE = 0x18
|
||||
XFRM_MSG_UPDPOLICY = 0x19
|
||||
XFRM_MSG_UPDSA = 0x1a
|
||||
XFRM_MSG_POLEXPIRE = 0x1b
|
||||
XFRM_MSG_FLUSHSA = 0x1c
|
||||
XFRM_MSG_FLUSHPOLICY = 0x1d
|
||||
XFRM_MSG_NEWAE = 0x1e
|
||||
XFRM_MSG_GETAE = 0x1f
|
||||
XFRM_MSG_REPORT = 0x20
|
||||
XFRM_MSG_MIGRATE = 0x21
|
||||
XFRM_MSG_NEWSADINFO = 0x22
|
||||
XFRM_MSG_GETSADINFO = 0x23
|
||||
XFRM_MSG_NEWSPDINFO = 0x24
|
||||
XFRM_MSG_GETSPDINFO = 0x25
|
||||
XFRM_MSG_MAPPING = 0x26
|
||||
XFRM_MSG_MAX = 0x26
|
||||
XFRM_NR_MSGTYPES = 0x17
|
||||
)
|
||||
|
||||
// Attribute types
|
||||
const (
|
||||
/* Netlink message attributes. */
|
||||
XFRMA_UNSPEC = 0x00
|
||||
XFRMA_ALG_AUTH = 0x01 /* struct xfrm_algo */
|
||||
XFRMA_ALG_CRYPT = 0x02 /* struct xfrm_algo */
|
||||
XFRMA_ALG_COMP = 0x03 /* struct xfrm_algo */
|
||||
XFRMA_ENCAP = 0x04 /* struct xfrm_algo + struct xfrm_encap_tmpl */
|
||||
XFRMA_TMPL = 0x05 /* 1 or more struct xfrm_user_tmpl */
|
||||
XFRMA_SA = 0x06 /* struct xfrm_usersa_info */
|
||||
XFRMA_POLICY = 0x07 /* struct xfrm_userpolicy_info */
|
||||
XFRMA_SEC_CTX = 0x08 /* struct xfrm_sec_ctx */
|
||||
XFRMA_LTIME_VAL = 0x09
|
||||
XFRMA_REPLAY_VAL = 0x0a
|
||||
XFRMA_REPLAY_THRESH = 0x0b
|
||||
XFRMA_ETIMER_THRESH = 0x0c
|
||||
XFRMA_SRCADDR = 0x0d /* xfrm_address_t */
|
||||
XFRMA_COADDR = 0x0e /* xfrm_address_t */
|
||||
XFRMA_LASTUSED = 0x0f /* unsigned long */
|
||||
XFRMA_POLICY_TYPE = 0x10 /* struct xfrm_userpolicy_type */
|
||||
XFRMA_MIGRATE = 0x11
|
||||
XFRMA_ALG_AEAD = 0x12 /* struct xfrm_algo_aead */
|
||||
XFRMA_KMADDRESS = 0x13 /* struct xfrm_user_kmaddress */
|
||||
XFRMA_ALG_AUTH_TRUNC = 0x14 /* struct xfrm_algo_auth */
|
||||
XFRMA_MARK = 0x15 /* struct xfrm_mark */
|
||||
XFRMA_TFCPAD = 0x16 /* __u32 */
|
||||
XFRMA_REPLAY_ESN_VAL = 0x17 /* struct xfrm_replay_esn */
|
||||
XFRMA_SA_EXTRA_FLAGS = 0x18 /* __u32 */
|
||||
XFRMA_MAX = 0x18
|
||||
)
|
||||
|
||||
const (
|
||||
SizeofXfrmAddress = 0x10
|
||||
SizeofXfrmSelector = 0x38
|
||||
SizeofXfrmLifetimeCfg = 0x40
|
||||
SizeofXfrmLifetimeCur = 0x20
|
||||
SizeofXfrmId = 0x18
|
||||
)
|
||||
|
||||
// typedef union {
|
||||
// __be32 a4;
|
||||
// __be32 a6[4];
|
||||
// } xfrm_address_t;
|
||||
|
||||
type XfrmAddress [SizeofXfrmAddress]byte
|
||||
|
||||
func (x *XfrmAddress) ToIP() net.IP {
|
||||
var empty = [12]byte{}
|
||||
ip := make(net.IP, net.IPv6len)
|
||||
if bytes.Equal(x[4:16], empty[:]) {
|
||||
ip[10] = 0xff
|
||||
ip[11] = 0xff
|
||||
copy(ip[12:16], x[0:4])
|
||||
} else {
|
||||
copy(ip[:], x[:])
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet {
|
||||
ip := x.ToIP()
|
||||
if GetIPFamily(ip) == FAMILY_V4 {
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)}
|
||||
}
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
|
||||
}
|
||||
|
||||
func (x *XfrmAddress) FromIP(ip net.IP) {
|
||||
var empty = [16]byte{}
|
||||
if len(ip) < net.IPv4len {
|
||||
copy(x[4:16], empty[:])
|
||||
} else if GetIPFamily(ip) == FAMILY_V4 {
|
||||
copy(x[0:4], ip.To4()[0:4])
|
||||
copy(x[4:16], empty[:12])
|
||||
} else {
|
||||
copy(x[0:16], ip.To16()[0:16])
|
||||
}
|
||||
}
|
||||
|
||||
func DeserializeXfrmAddress(b []byte) *XfrmAddress {
|
||||
return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0]))
|
||||
}
|
||||
|
||||
func (x *XfrmAddress) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_selector {
|
||||
// xfrm_address_t daddr;
|
||||
// xfrm_address_t saddr;
|
||||
// __be16 dport;
|
||||
// __be16 dport_mask;
|
||||
// __be16 sport;
|
||||
// __be16 sport_mask;
|
||||
// __u16 family;
|
||||
// __u8 prefixlen_d;
|
||||
// __u8 prefixlen_s;
|
||||
// __u8 proto;
|
||||
// int ifindex;
|
||||
// __kernel_uid32_t user;
|
||||
// };
|
||||
|
||||
type XfrmSelector struct {
|
||||
Daddr XfrmAddress
|
||||
Saddr XfrmAddress
|
||||
Dport uint16 // big endian
|
||||
DportMask uint16 // big endian
|
||||
Sport uint16 // big endian
|
||||
SportMask uint16 // big endian
|
||||
Family uint16
|
||||
PrefixlenD uint8
|
||||
PrefixlenS uint8
|
||||
Proto uint8
|
||||
Pad [3]byte
|
||||
Ifindex int32
|
||||
User uint32
|
||||
}
|
||||
|
||||
func (msg *XfrmSelector) Len() int {
|
||||
return SizeofXfrmSelector
|
||||
}
|
||||
|
||||
func DeserializeXfrmSelector(b []byte) *XfrmSelector {
|
||||
return (*XfrmSelector)(unsafe.Pointer(&b[0:SizeofXfrmSelector][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmSelector) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmSelector]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_lifetime_cfg {
|
||||
// __u64 soft_byte_limit;
|
||||
// __u64 hard_byte_limit;
|
||||
// __u64 soft_packet_limit;
|
||||
// __u64 hard_packet_limit;
|
||||
// __u64 soft_add_expires_seconds;
|
||||
// __u64 hard_add_expires_seconds;
|
||||
// __u64 soft_use_expires_seconds;
|
||||
// __u64 hard_use_expires_seconds;
|
||||
// };
|
||||
//
|
||||
|
||||
type XfrmLifetimeCfg struct {
|
||||
SoftByteLimit uint64
|
||||
HardByteLimit uint64
|
||||
SoftPacketLimit uint64
|
||||
HardPacketLimit uint64
|
||||
SoftAddExpiresSeconds uint64
|
||||
HardAddExpiresSeconds uint64
|
||||
SoftUseExpiresSeconds uint64
|
||||
HardUseExpiresSeconds uint64
|
||||
}
|
||||
|
||||
func (msg *XfrmLifetimeCfg) Len() int {
|
||||
return SizeofXfrmLifetimeCfg
|
||||
}
|
||||
|
||||
func DeserializeXfrmLifetimeCfg(b []byte) *XfrmLifetimeCfg {
|
||||
return (*XfrmLifetimeCfg)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCfg][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmLifetimeCfg) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmLifetimeCfg]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_lifetime_cur {
|
||||
// __u64 bytes;
|
||||
// __u64 packets;
|
||||
// __u64 add_time;
|
||||
// __u64 use_time;
|
||||
// };
|
||||
|
||||
type XfrmLifetimeCur struct {
|
||||
Bytes uint64
|
||||
Packets uint64
|
||||
AddTime uint64
|
||||
UseTime uint64
|
||||
}
|
||||
|
||||
func (msg *XfrmLifetimeCur) Len() int {
|
||||
return SizeofXfrmLifetimeCur
|
||||
}
|
||||
|
||||
func DeserializeXfrmLifetimeCur(b []byte) *XfrmLifetimeCur {
|
||||
return (*XfrmLifetimeCur)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCur][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmLifetimeCur) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmLifetimeCur]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_id {
|
||||
// xfrm_address_t daddr;
|
||||
// __be32 spi;
|
||||
// __u8 proto;
|
||||
// };
|
||||
|
||||
type XfrmId struct {
|
||||
Daddr XfrmAddress
|
||||
Spi uint32 // big endian
|
||||
Proto uint8
|
||||
Pad [3]byte
|
||||
}
|
||||
|
||||
func (msg *XfrmId) Len() int {
|
||||
return SizeofXfrmId
|
||||
}
|
||||
|
||||
func DeserializeXfrmId(b []byte) *XfrmId {
|
||||
return (*XfrmId)(unsafe.Pointer(&b[0:SizeofXfrmId][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmId) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmId]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package nl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
SizeofXfrmUserpolicyId = 0x40
|
||||
SizeofXfrmUserpolicyInfo = 0xa8
|
||||
SizeofXfrmUserTmpl = 0x40
|
||||
)
|
||||
|
||||
// struct xfrm_userpolicy_id {
|
||||
// struct xfrm_selector sel;
|
||||
// __u32 index;
|
||||
// __u8 dir;
|
||||
// };
|
||||
//
|
||||
|
||||
type XfrmUserpolicyId struct {
|
||||
Sel XfrmSelector
|
||||
Index uint32
|
||||
Dir uint8
|
||||
Pad [3]byte
|
||||
}
|
||||
|
||||
func (msg *XfrmUserpolicyId) Len() int {
|
||||
return SizeofXfrmUserpolicyId
|
||||
}
|
||||
|
||||
func DeserializeXfrmUserpolicyId(b []byte) *XfrmUserpolicyId {
|
||||
return (*XfrmUserpolicyId)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyId][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmUserpolicyId) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmUserpolicyId]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_userpolicy_info {
|
||||
// struct xfrm_selector sel;
|
||||
// struct xfrm_lifetime_cfg lft;
|
||||
// struct xfrm_lifetime_cur curlft;
|
||||
// __u32 priority;
|
||||
// __u32 index;
|
||||
// __u8 dir;
|
||||
// __u8 action;
|
||||
// #define XFRM_POLICY_ALLOW 0
|
||||
// #define XFRM_POLICY_BLOCK 1
|
||||
// __u8 flags;
|
||||
// #define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */
|
||||
// /* Automatically expand selector to include matching ICMP payloads. */
|
||||
// #define XFRM_POLICY_ICMP 2
|
||||
// __u8 share;
|
||||
// };
|
||||
|
||||
type XfrmUserpolicyInfo struct {
|
||||
Sel XfrmSelector
|
||||
Lft XfrmLifetimeCfg
|
||||
Curlft XfrmLifetimeCur
|
||||
Priority uint32
|
||||
Index uint32
|
||||
Dir uint8
|
||||
Action uint8
|
||||
Flags uint8
|
||||
Share uint8
|
||||
Pad [4]byte
|
||||
}
|
||||
|
||||
func (msg *XfrmUserpolicyInfo) Len() int {
|
||||
return SizeofXfrmUserpolicyInfo
|
||||
}
|
||||
|
||||
func DeserializeXfrmUserpolicyInfo(b []byte) *XfrmUserpolicyInfo {
|
||||
return (*XfrmUserpolicyInfo)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyInfo][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmUserpolicyInfo) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmUserpolicyInfo]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_user_tmpl {
|
||||
// struct xfrm_id id;
|
||||
// __u16 family;
|
||||
// xfrm_address_t saddr;
|
||||
// __u32 reqid;
|
||||
// __u8 mode;
|
||||
// __u8 share;
|
||||
// __u8 optional;
|
||||
// __u32 aalgos;
|
||||
// __u32 ealgos;
|
||||
// __u32 calgos;
|
||||
// }
|
||||
|
||||
type XfrmUserTmpl struct {
|
||||
XfrmId XfrmId
|
||||
Family uint16
|
||||
Pad1 [2]byte
|
||||
Saddr XfrmAddress
|
||||
Reqid uint32
|
||||
Mode uint8
|
||||
Share uint8
|
||||
Optional uint8
|
||||
Pad2 byte
|
||||
Aalgos uint32
|
||||
Ealgos uint32
|
||||
Calgos uint32
|
||||
}
|
||||
|
||||
func (msg *XfrmUserTmpl) Len() int {
|
||||
return SizeofXfrmUserTmpl
|
||||
}
|
||||
|
||||
func DeserializeXfrmUserTmpl(b []byte) *XfrmUserTmpl {
|
||||
return (*XfrmUserTmpl)(unsafe.Pointer(&b[0:SizeofXfrmUserTmpl][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmUserTmpl) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmUserTmpl]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
package nl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
SizeofXfrmUsersaId = 0x18
|
||||
SizeofXfrmStats = 0x0c
|
||||
SizeofXfrmUsersaInfo = 0xe0
|
||||
SizeofXfrmAlgo = 0x44
|
||||
SizeofXfrmAlgoAuth = 0x48
|
||||
SizeofXfrmEncapTmpl = 0x18
|
||||
)
|
||||
|
||||
// struct xfrm_usersa_id {
|
||||
// xfrm_address_t daddr;
|
||||
// __be32 spi;
|
||||
// __u16 family;
|
||||
// __u8 proto;
|
||||
// };
|
||||
|
||||
type XfrmUsersaId struct {
|
||||
Daddr XfrmAddress
|
||||
Spi uint32 // big endian
|
||||
Family uint16
|
||||
Proto uint8
|
||||
Pad byte
|
||||
}
|
||||
|
||||
func (msg *XfrmUsersaId) Len() int {
|
||||
return SizeofXfrmUsersaId
|
||||
}
|
||||
|
||||
func DeserializeXfrmUsersaId(b []byte) *XfrmUsersaId {
|
||||
return (*XfrmUsersaId)(unsafe.Pointer(&b[0:SizeofXfrmUsersaId][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmUsersaId) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmUsersaId]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_stats {
|
||||
// __u32 replay_window;
|
||||
// __u32 replay;
|
||||
// __u32 integrity_failed;
|
||||
// };
|
||||
|
||||
type XfrmStats struct {
|
||||
ReplayWindow uint32
|
||||
Replay uint32
|
||||
IntegrityFailed uint32
|
||||
}
|
||||
|
||||
func (msg *XfrmStats) Len() int {
|
||||
return SizeofXfrmStats
|
||||
}
|
||||
|
||||
func DeserializeXfrmStats(b []byte) *XfrmStats {
|
||||
return (*XfrmStats)(unsafe.Pointer(&b[0:SizeofXfrmStats][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmStats) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmStats]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_usersa_info {
|
||||
// struct xfrm_selector sel;
|
||||
// struct xfrm_id id;
|
||||
// xfrm_address_t saddr;
|
||||
// struct xfrm_lifetime_cfg lft;
|
||||
// struct xfrm_lifetime_cur curlft;
|
||||
// struct xfrm_stats stats;
|
||||
// __u32 seq;
|
||||
// __u32 reqid;
|
||||
// __u16 family;
|
||||
// __u8 mode; /* XFRM_MODE_xxx */
|
||||
// __u8 replay_window;
|
||||
// __u8 flags;
|
||||
// #define XFRM_STATE_NOECN 1
|
||||
// #define XFRM_STATE_DECAP_DSCP 2
|
||||
// #define XFRM_STATE_NOPMTUDISC 4
|
||||
// #define XFRM_STATE_WILDRECV 8
|
||||
// #define XFRM_STATE_ICMP 16
|
||||
// #define XFRM_STATE_AF_UNSPEC 32
|
||||
// #define XFRM_STATE_ALIGN4 64
|
||||
// #define XFRM_STATE_ESN 128
|
||||
// };
|
||||
//
|
||||
// #define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1
|
||||
//
|
||||
|
||||
type XfrmUsersaInfo struct {
|
||||
Sel XfrmSelector
|
||||
Id XfrmId
|
||||
Saddr XfrmAddress
|
||||
Lft XfrmLifetimeCfg
|
||||
Curlft XfrmLifetimeCur
|
||||
Stats XfrmStats
|
||||
Seq uint32
|
||||
Reqid uint32
|
||||
Family uint16
|
||||
Mode uint8
|
||||
ReplayWindow uint8
|
||||
Flags uint8
|
||||
Pad [7]byte
|
||||
}
|
||||
|
||||
func (msg *XfrmUsersaInfo) Len() int {
|
||||
return SizeofXfrmUsersaInfo
|
||||
}
|
||||
|
||||
func DeserializeXfrmUsersaInfo(b []byte) *XfrmUsersaInfo {
|
||||
return (*XfrmUsersaInfo)(unsafe.Pointer(&b[0:SizeofXfrmUsersaInfo][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmUsersaInfo) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmUsersaInfo]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_algo {
|
||||
// char alg_name[64];
|
||||
// unsigned int alg_key_len; /* in bits */
|
||||
// char alg_key[0];
|
||||
// };
|
||||
|
||||
type XfrmAlgo struct {
|
||||
AlgName [64]byte
|
||||
AlgKeyLen uint32
|
||||
AlgKey []byte
|
||||
}
|
||||
|
||||
func (msg *XfrmAlgo) Len() int {
|
||||
return SizeofXfrmAlgo + int(msg.AlgKeyLen/8)
|
||||
}
|
||||
|
||||
func DeserializeXfrmAlgo(b []byte) *XfrmAlgo {
|
||||
ret := XfrmAlgo{}
|
||||
copy(ret.AlgName[:], b[0:64])
|
||||
ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64]))
|
||||
ret.AlgKey = b[68:ret.Len()]
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (msg *XfrmAlgo) Serialize() []byte {
|
||||
b := make([]byte, msg.Len())
|
||||
copy(b[0:64], msg.AlgName[:])
|
||||
copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:])
|
||||
copy(b[68:msg.Len()], msg.AlgKey[:])
|
||||
return b
|
||||
}
|
||||
|
||||
// struct xfrm_algo_auth {
|
||||
// char alg_name[64];
|
||||
// unsigned int alg_key_len; /* in bits */
|
||||
// unsigned int alg_trunc_len; /* in bits */
|
||||
// char alg_key[0];
|
||||
// };
|
||||
|
||||
type XfrmAlgoAuth struct {
|
||||
AlgName [64]byte
|
||||
AlgKeyLen uint32
|
||||
AlgTruncLen uint32
|
||||
AlgKey []byte
|
||||
}
|
||||
|
||||
func (msg *XfrmAlgoAuth) Len() int {
|
||||
return SizeofXfrmAlgoAuth + int(msg.AlgKeyLen/8)
|
||||
}
|
||||
|
||||
func DeserializeXfrmAlgoAuth(b []byte) *XfrmAlgoAuth {
|
||||
ret := XfrmAlgoAuth{}
|
||||
copy(ret.AlgName[:], b[0:64])
|
||||
ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64]))
|
||||
ret.AlgTruncLen = *(*uint32)(unsafe.Pointer(&b[68]))
|
||||
ret.AlgKey = b[72:ret.Len()]
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (msg *XfrmAlgoAuth) Serialize() []byte {
|
||||
b := make([]byte, msg.Len())
|
||||
copy(b[0:64], msg.AlgName[:])
|
||||
copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:])
|
||||
copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgTruncLen)))[:])
|
||||
copy(b[72:msg.Len()], msg.AlgKey[:])
|
||||
return b
|
||||
}
|
||||
|
||||
// struct xfrm_algo_aead {
|
||||
// char alg_name[64];
|
||||
// unsigned int alg_key_len; /* in bits */
|
||||
// unsigned int alg_icv_len; /* in bits */
|
||||
// char alg_key[0];
|
||||
// }
|
||||
|
||||
// struct xfrm_encap_tmpl {
|
||||
// __u16 encap_type;
|
||||
// __be16 encap_sport;
|
||||
// __be16 encap_dport;
|
||||
// xfrm_address_t encap_oa;
|
||||
// };
|
||||
|
||||
type XfrmEncapTmpl struct {
|
||||
EncapType uint16
|
||||
EncapSport uint16 // big endian
|
||||
EncapDport uint16 // big endian
|
||||
Pad [2]byte
|
||||
EncapOa XfrmAddress
|
||||
}
|
||||
|
||||
func (msg *XfrmEncapTmpl) Len() int {
|
||||
return SizeofXfrmEncapTmpl
|
||||
}
|
||||
|
||||
func DeserializeXfrmEncapTmpl(b []byte) *XfrmEncapTmpl {
|
||||
return (*XfrmEncapTmpl)(unsafe.Pointer(&b[0:SizeofXfrmEncapTmpl][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmEncapTmpl) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmEncapTmpl]byte)(unsafe.Pointer(msg)))[:]
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Protinfo represents bridge flags from netlink.
|
||||
type Protinfo struct {
|
||||
Hairpin bool
|
||||
Guard bool
|
||||
FastLeave bool
|
||||
RootBlock bool
|
||||
Learning bool
|
||||
Flood bool
|
||||
}
|
||||
|
||||
// String returns a list of enabled flags
|
||||
func (prot *Protinfo) String() string {
|
||||
var boolStrings []string
|
||||
if prot.Hairpin {
|
||||
boolStrings = append(boolStrings, "Hairpin")
|
||||
}
|
||||
if prot.Guard {
|
||||
boolStrings = append(boolStrings, "Guard")
|
||||
}
|
||||
if prot.FastLeave {
|
||||
boolStrings = append(boolStrings, "FastLeave")
|
||||
}
|
||||
if prot.RootBlock {
|
||||
boolStrings = append(boolStrings, "RootBlock")
|
||||
}
|
||||
if prot.Learning {
|
||||
boolStrings = append(boolStrings, "Learning")
|
||||
}
|
||||
if prot.Flood {
|
||||
boolStrings = append(boolStrings, "Flood")
|
||||
}
|
||||
return strings.Join(boolStrings, " ")
|
||||
}
|
||||
|
||||
func boolToByte(x bool) []byte {
|
||||
if x {
|
||||
return []byte{1}
|
||||
}
|
||||
return []byte{0}
|
||||
}
|
||||
|
||||
func byteToBool(x byte) bool {
|
||||
if uint8(x) != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
func LinkGetProtinfo(link Link) (Protinfo, error) {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
var pi Protinfo
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
|
||||
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
|
||||
req.AddData(msg)
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
if err != nil {
|
||||
return pi, err
|
||||
}
|
||||
|
||||
for _, m := range msgs {
|
||||
ans := nl.DeserializeIfInfomsg(m)
|
||||
if int(ans.Index) != base.Index {
|
||||
continue
|
||||
}
|
||||
attrs, err := nl.ParseRouteAttr(m[ans.Len():])
|
||||
if err != nil {
|
||||
return pi, err
|
||||
}
|
||||
for _, attr := range attrs {
|
||||
if attr.Attr.Type != syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED {
|
||||
continue
|
||||
}
|
||||
infos, err := nl.ParseRouteAttr(attr.Value)
|
||||
if err != nil {
|
||||
return pi, err
|
||||
}
|
||||
var pi Protinfo
|
||||
for _, info := range infos {
|
||||
switch info.Attr.Type {
|
||||
case nl.IFLA_BRPORT_MODE:
|
||||
pi.Hairpin = byteToBool(info.Value[0])
|
||||
case nl.IFLA_BRPORT_GUARD:
|
||||
pi.Guard = byteToBool(info.Value[0])
|
||||
case nl.IFLA_BRPORT_FAST_LEAVE:
|
||||
pi.FastLeave = byteToBool(info.Value[0])
|
||||
case nl.IFLA_BRPORT_PROTECT:
|
||||
pi.RootBlock = byteToBool(info.Value[0])
|
||||
case nl.IFLA_BRPORT_LEARNING:
|
||||
pi.Learning = byteToBool(info.Value[0])
|
||||
case nl.IFLA_BRPORT_UNICAST_FLOOD:
|
||||
pi.Flood = byteToBool(info.Value[0])
|
||||
}
|
||||
}
|
||||
return pi, nil
|
||||
}
|
||||
}
|
||||
return pi, fmt.Errorf("Device with index %d not found", base.Index)
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
HANDLE_NONE = 0
|
||||
HANDLE_INGRESS = 0xFFFFFFF1
|
||||
HANDLE_ROOT = 0xFFFFFFFF
|
||||
PRIORITY_MAP_LEN = 16
|
||||
)
|
||||
|
||||
type Qdisc interface {
|
||||
Attrs() *QdiscAttrs
|
||||
Type() string
|
||||
}
|
||||
|
||||
// Qdisc represents a netlink qdisc. A qdisc is associated with a link,
|
||||
// has a handle, a parent and a refcnt. The root qdisc of a device should
|
||||
// have parent == HANDLE_ROOT.
|
||||
type QdiscAttrs struct {
|
||||
LinkIndex int
|
||||
Handle uint32
|
||||
Parent uint32
|
||||
Refcnt uint32 // read only
|
||||
}
|
||||
|
||||
func (q QdiscAttrs) String() string {
|
||||
return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Refcnt: %s}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Refcnt)
|
||||
}
|
||||
|
||||
func MakeHandle(major, minor uint16) uint32 {
|
||||
return (uint32(major) << 16) | uint32(minor)
|
||||
}
|
||||
|
||||
func MajorMinor(handle uint32) (uint16, uint16) {
|
||||
return uint16((handle & 0xFFFF0000) >> 16), uint16(handle & 0x0000FFFFF)
|
||||
}
|
||||
|
||||
func HandleStr(handle uint32) string {
|
||||
switch handle {
|
||||
case HANDLE_NONE:
|
||||
return "none"
|
||||
case HANDLE_INGRESS:
|
||||
return "ingress"
|
||||
case HANDLE_ROOT:
|
||||
return "root"
|
||||
default:
|
||||
major, minor := MajorMinor(handle)
|
||||
return fmt.Sprintf("%x:%x", major, minor)
|
||||
}
|
||||
}
|
||||
|
||||
// PfifoFast is the default qdisc created by the kernel if one has not
|
||||
// been defined for the interface
|
||||
type PfifoFast struct {
|
||||
QdiscAttrs
|
||||
Bands uint8
|
||||
PriorityMap [PRIORITY_MAP_LEN]uint8
|
||||
}
|
||||
|
||||
func (qdisc *PfifoFast) Attrs() *QdiscAttrs {
|
||||
return &qdisc.QdiscAttrs
|
||||
}
|
||||
|
||||
func (qdisc *PfifoFast) Type() string {
|
||||
return "pfifo_fast"
|
||||
}
|
||||
|
||||
// Prio is a basic qdisc that works just like PfifoFast
|
||||
type Prio struct {
|
||||
QdiscAttrs
|
||||
Bands uint8
|
||||
PriorityMap [PRIORITY_MAP_LEN]uint8
|
||||
}
|
||||
|
||||
func NewPrio(attrs QdiscAttrs) *Prio {
|
||||
return &Prio{
|
||||
QdiscAttrs: attrs,
|
||||
Bands: 3,
|
||||
PriorityMap: [PRIORITY_MAP_LEN]uint8{1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
}
|
||||
}
|
||||
|
||||
func (qdisc *Prio) Attrs() *QdiscAttrs {
|
||||
return &qdisc.QdiscAttrs
|
||||
}
|
||||
|
||||
func (qdisc *Prio) Type() string {
|
||||
return "prio"
|
||||
}
|
||||
|
||||
// Tbf is a classful qdisc that rate limits based on tokens
|
||||
type Tbf struct {
|
||||
QdiscAttrs
|
||||
// TODO: handle 64bit rate properly
|
||||
Rate uint64
|
||||
Limit uint32
|
||||
Buffer uint32
|
||||
// TODO: handle other settings
|
||||
}
|
||||
|
||||
func (qdisc *Tbf) Attrs() *QdiscAttrs {
|
||||
return &qdisc.QdiscAttrs
|
||||
}
|
||||
|
||||
func (qdisc *Tbf) Type() string {
|
||||
return "tbf"
|
||||
}
|
||||
|
||||
// Ingress is a qdisc for adding ingress filters
|
||||
type Ingress struct {
|
||||
QdiscAttrs
|
||||
}
|
||||
|
||||
func (qdisc *Ingress) Attrs() *QdiscAttrs {
|
||||
return &qdisc.QdiscAttrs
|
||||
}
|
||||
|
||||
func (qdisc *Ingress) Type() string {
|
||||
return "ingress"
|
||||
}
|
||||
|
||||
// GenericQdisc qdiscs represent types that are not currently understood
|
||||
// by this netlink library.
|
||||
type GenericQdisc struct {
|
||||
QdiscAttrs
|
||||
QdiscType string
|
||||
}
|
||||
|
||||
func (qdisc *GenericQdisc) Attrs() *QdiscAttrs {
|
||||
return &qdisc.QdiscAttrs
|
||||
}
|
||||
|
||||
func (qdisc *GenericQdisc) Type() string {
|
||||
return qdisc.QdiscType
|
||||
}
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// QdiscDel will delete a qdisc from the system.
|
||||
// Equivalent to: `tc qdisc del $qdisc`
|
||||
func QdiscDel(qdisc Qdisc) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_DELQDISC, syscall.NLM_F_ACK)
|
||||
base := qdisc.Attrs()
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: int32(base.LinkIndex),
|
||||
Handle: base.Handle,
|
||||
Parent: base.Parent,
|
||||
}
|
||||
req.AddData(msg)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// QdiscAdd will add a qdisc to the system.
|
||||
// Equivalent to: `tc qdisc add $qdisc`
|
||||
func QdiscAdd(qdisc Qdisc) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWQDISC, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
base := qdisc.Attrs()
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: int32(base.LinkIndex),
|
||||
Handle: base.Handle,
|
||||
Parent: base.Parent,
|
||||
}
|
||||
req.AddData(msg)
|
||||
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type())))
|
||||
|
||||
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
|
||||
if prio, ok := qdisc.(*Prio); ok {
|
||||
tcmap := nl.TcPrioMap{
|
||||
Bands: int32(prio.Bands),
|
||||
Priomap: prio.PriorityMap,
|
||||
}
|
||||
options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize())
|
||||
} else if tbf, ok := qdisc.(*Tbf); ok {
|
||||
opt := nl.TcTbfQopt{}
|
||||
// TODO: handle rate > uint32
|
||||
opt.Rate.Rate = uint32(tbf.Rate)
|
||||
opt.Limit = tbf.Limit
|
||||
opt.Buffer = tbf.Buffer
|
||||
nl.NewRtAttrChild(options, nl.TCA_TBF_PARMS, opt.Serialize())
|
||||
} else if _, ok := qdisc.(*Ingress); ok {
|
||||
// ingress filters must use the proper handle
|
||||
if msg.Parent != HANDLE_INGRESS {
|
||||
return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS")
|
||||
}
|
||||
}
|
||||
req.AddData(options)
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// QdiscList gets a list of qdiscs in the system.
|
||||
// Equivalent to: `tc qdisc show`.
|
||||
// The list can be filtered by link.
|
||||
func QdiscList(link Link) ([]Qdisc, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETQDISC, syscall.NLM_F_DUMP)
|
||||
index := int32(0)
|
||||
if link != nil {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
index = int32(base.Index)
|
||||
}
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: index,
|
||||
}
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWQDISC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []Qdisc
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeTcMsg(m)
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// skip qdiscs from other interfaces
|
||||
if link != nil && msg.Ifindex != index {
|
||||
continue
|
||||
}
|
||||
|
||||
base := QdiscAttrs{
|
||||
LinkIndex: int(msg.Ifindex),
|
||||
Handle: msg.Handle,
|
||||
Parent: msg.Parent,
|
||||
Refcnt: msg.Info,
|
||||
}
|
||||
var qdisc Qdisc
|
||||
qdiscType := ""
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case nl.TCA_KIND:
|
||||
qdiscType = string(attr.Value[:len(attr.Value)-1])
|
||||
switch qdiscType {
|
||||
case "pfifo_fast":
|
||||
qdisc = &PfifoFast{}
|
||||
case "prio":
|
||||
qdisc = &Prio{}
|
||||
case "tbf":
|
||||
qdisc = &Tbf{}
|
||||
case "ingress":
|
||||
qdisc = &Ingress{}
|
||||
default:
|
||||
qdisc = &GenericQdisc{QdiscType: qdiscType}
|
||||
}
|
||||
case nl.TCA_OPTIONS:
|
||||
switch qdiscType {
|
||||
case "pfifo_fast":
|
||||
// pfifo returns TcPrioMap directly without wrapping it in rtattr
|
||||
if err := parsePfifoFastData(qdisc, attr.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "prio":
|
||||
// prio returns TcPrioMap directly without wrapping it in rtattr
|
||||
if err := parsePrioData(qdisc, attr.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "tbf":
|
||||
data, err := nl.ParseRouteAttr(attr.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseTbfData(qdisc, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// no options for ingress
|
||||
}
|
||||
}
|
||||
}
|
||||
*qdisc.Attrs() = base
|
||||
res = append(res, qdisc)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parsePfifoFastData(qdisc Qdisc, value []byte) error {
|
||||
pfifo := qdisc.(*PfifoFast)
|
||||
tcmap := nl.DeserializeTcPrioMap(value)
|
||||
pfifo.PriorityMap = tcmap.Priomap
|
||||
pfifo.Bands = uint8(tcmap.Bands)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parsePrioData(qdisc Qdisc, value []byte) error {
|
||||
prio := qdisc.(*Prio)
|
||||
tcmap := nl.DeserializeTcPrioMap(value)
|
||||
prio.PriorityMap = tcmap.Priomap
|
||||
prio.Bands = uint8(tcmap.Bands)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
|
||||
native = nl.NativeEndian()
|
||||
tbf := qdisc.(*Tbf)
|
||||
for _, datum := range data {
|
||||
switch datum.Attr.Type {
|
||||
case nl.TCA_TBF_PARMS:
|
||||
opt := nl.DeserializeTcTbfQopt(datum.Value)
|
||||
tbf.Rate = uint64(opt.Rate.Rate)
|
||||
tbf.Limit = opt.Limit
|
||||
tbf.Buffer = opt.Buffer
|
||||
case nl.TCA_TBF_RATE64:
|
||||
tbf.Rate = native.Uint64(datum.Value[0:4])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
TIME_UNITS_PER_SEC = 1000000
|
||||
)
|
||||
|
||||
var (
|
||||
tickInUsec float64 = 0.0
|
||||
clockFactor float64 = 0.0
|
||||
)
|
||||
|
||||
func initClock() {
|
||||
data, err := ioutil.ReadFile("/proc/net/psched")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
parts := strings.Split(strings.TrimSpace(string(data)), " ")
|
||||
if len(parts) < 3 {
|
||||
return
|
||||
}
|
||||
var vals [3]uint64
|
||||
for i := range vals {
|
||||
val, err := strconv.ParseUint(parts[i], 16, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
vals[i] = val
|
||||
}
|
||||
// compatibility
|
||||
if vals[2] == 1000000000 {
|
||||
vals[0] = vals[1]
|
||||
}
|
||||
clockFactor = float64(vals[2]) / TIME_UNITS_PER_SEC
|
||||
tickInUsec = float64(vals[0]) / float64(vals[1]) * clockFactor
|
||||
}
|
||||
|
||||
func TickInUsec() float64 {
|
||||
if tickInUsec == 0.0 {
|
||||
initClock()
|
||||
}
|
||||
return tickInUsec
|
||||
}
|
||||
|
||||
func ClockFactor() float64 {
|
||||
if clockFactor == 0.0 {
|
||||
initClock()
|
||||
}
|
||||
return clockFactor
|
||||
}
|
||||
|
||||
func time2Tick(time uint32) uint32 {
|
||||
return uint32(float64(time) * TickInUsec())
|
||||
}
|
||||
|
||||
func tick2Time(tick uint32) uint32 {
|
||||
return uint32(float64(tick) / TickInUsec())
|
||||
}
|
||||
|
||||
func time2Ktime(time uint32) uint32 {
|
||||
return uint32(float64(time) * ClockFactor())
|
||||
}
|
||||
|
||||
func ktime2Time(ktime uint32) uint32 {
|
||||
return uint32(float64(ktime) / ClockFactor())
|
||||
}
|
||||
|
||||
func burst(rate uint64, buffer uint32) uint32 {
|
||||
return uint32(float64(rate) * float64(tick2Time(buffer)) / TIME_UNITS_PER_SEC)
|
||||
}
|
||||
|
||||
func latency(rate uint64, limit, buffer uint32) float64 {
|
||||
return TIME_UNITS_PER_SEC*(float64(limit)/float64(rate)) - float64(tick2Time(buffer))
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Scope is an enum representing a route scope.
|
||||
type Scope uint8
|
||||
|
||||
const (
|
||||
SCOPE_UNIVERSE Scope = syscall.RT_SCOPE_UNIVERSE
|
||||
SCOPE_SITE Scope = syscall.RT_SCOPE_SITE
|
||||
SCOPE_LINK Scope = syscall.RT_SCOPE_LINK
|
||||
SCOPE_HOST Scope = syscall.RT_SCOPE_HOST
|
||||
SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE
|
||||
)
|
||||
|
||||
// Route represents a netlink route. A route is associated with a link,
|
||||
// has a destination network, an optional source ip, and optional
|
||||
// gateway. Advanced route parameters and non-main routing tables are
|
||||
// currently not supported.
|
||||
type Route struct {
|
||||
LinkIndex int
|
||||
Scope Scope
|
||||
Dst *net.IPNet
|
||||
Src net.IP
|
||||
Gw net.IP
|
||||
}
|
||||
|
||||
func (r Route) String() string {
|
||||
return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s}", r.LinkIndex, r.Dst,
|
||||
r.Src, r.Gw)
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// RtAttr is shared so it is in netlink_linux.go
|
||||
|
||||
// RouteAdd will add a route to the system.
|
||||
// Equivalent to: `ip route add $route`
|
||||
func RouteAdd(route *Route) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
return routeHandle(route, req, nl.NewRtMsg())
|
||||
}
|
||||
|
||||
// RouteAdd will delete a route from the system.
|
||||
// Equivalent to: `ip route del $route`
|
||||
func RouteDel(route *Route) error {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK)
|
||||
return routeHandle(route, req, nl.NewRtDelMsg())
|
||||
}
|
||||
|
||||
func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
|
||||
if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil {
|
||||
return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
|
||||
}
|
||||
|
||||
msg.Scope = uint8(route.Scope)
|
||||
family := -1
|
||||
var rtAttrs []*nl.RtAttr
|
||||
|
||||
if route.Dst != nil && route.Dst.IP != nil {
|
||||
dstLen, _ := route.Dst.Mask.Size()
|
||||
msg.Dst_len = uint8(dstLen)
|
||||
dstFamily := nl.GetIPFamily(route.Dst.IP)
|
||||
family = dstFamily
|
||||
var dstData []byte
|
||||
if dstFamily == FAMILY_V4 {
|
||||
dstData = route.Dst.IP.To4()
|
||||
} else {
|
||||
dstData = route.Dst.IP.To16()
|
||||
}
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
|
||||
}
|
||||
|
||||
if route.Src != nil {
|
||||
srcFamily := nl.GetIPFamily(route.Src)
|
||||
if family != -1 && family != srcFamily {
|
||||
return fmt.Errorf("source and destination ip are not the same IP family")
|
||||
}
|
||||
family = srcFamily
|
||||
var srcData []byte
|
||||
if srcFamily == FAMILY_V4 {
|
||||
srcData = route.Src.To4()
|
||||
} else {
|
||||
srcData = route.Src.To16()
|
||||
}
|
||||
// The commonly used src ip for routes is actually PREFSRC
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PREFSRC, srcData))
|
||||
}
|
||||
|
||||
if route.Gw != nil {
|
||||
gwFamily := nl.GetIPFamily(route.Gw)
|
||||
if family != -1 && family != gwFamily {
|
||||
return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
|
||||
}
|
||||
family = gwFamily
|
||||
var gwData []byte
|
||||
if gwFamily == FAMILY_V4 {
|
||||
gwData = route.Gw.To4()
|
||||
} else {
|
||||
gwData = route.Gw.To16()
|
||||
}
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData))
|
||||
}
|
||||
|
||||
msg.Family = uint8(family)
|
||||
|
||||
req.AddData(msg)
|
||||
for _, attr := range rtAttrs {
|
||||
req.AddData(attr)
|
||||
}
|
||||
|
||||
var (
|
||||
b = make([]byte, 4)
|
||||
native = nl.NativeEndian()
|
||||
)
|
||||
native.PutUint32(b, uint32(route.LinkIndex))
|
||||
|
||||
req.AddData(nl.NewRtAttr(syscall.RTA_OIF, b))
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// RouteList gets a list of routes in the system.
|
||||
// Equivalent to: `ip route show`.
|
||||
// The list can be filtered by link and ip family.
|
||||
func RouteList(link Link, family int) ([]Route, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
|
||||
msg := nl.NewIfInfomsg(family)
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := 0
|
||||
if link != nil {
|
||||
base := link.Attrs()
|
||||
ensureIndex(base)
|
||||
index = base.Index
|
||||
}
|
||||
|
||||
native := nl.NativeEndian()
|
||||
var res []Route
|
||||
MsgLoop:
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeRtMsg(m)
|
||||
|
||||
if msg.Flags&syscall.RTM_F_CLONED != 0 {
|
||||
// Ignore cloned routes
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Table != syscall.RT_TABLE_MAIN {
|
||||
// Ignore non-main tables
|
||||
continue
|
||||
}
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
route := Route{Scope: Scope(msg.Scope)}
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case syscall.RTA_GATEWAY:
|
||||
route.Gw = net.IP(attr.Value)
|
||||
case syscall.RTA_PREFSRC:
|
||||
route.Src = net.IP(attr.Value)
|
||||
case syscall.RTA_DST:
|
||||
route.Dst = &net.IPNet{
|
||||
IP: attr.Value,
|
||||
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
|
||||
}
|
||||
case syscall.RTA_OIF:
|
||||
routeIndex := int(native.Uint32(attr.Value[0:4]))
|
||||
if link != nil && routeIndex != index {
|
||||
// Ignore routes from other interfaces
|
||||
continue MsgLoop
|
||||
}
|
||||
route.LinkIndex = routeIndex
|
||||
}
|
||||
}
|
||||
res = append(res, route)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// RouteGet gets a route to a specific destination from the host system.
|
||||
// Equivalent to: 'ip route get'.
|
||||
func RouteGet(destination net.IP) ([]Route, error) {
|
||||
req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST)
|
||||
family := nl.GetIPFamily(destination)
|
||||
var destinationData []byte
|
||||
var bitlen uint8
|
||||
if family == FAMILY_V4 {
|
||||
destinationData = destination.To4()
|
||||
bitlen = 32
|
||||
} else {
|
||||
destinationData = destination.To16()
|
||||
bitlen = 128
|
||||
}
|
||||
msg := &nl.RtMsg{}
|
||||
msg.Family = uint8(family)
|
||||
msg.Dst_len = bitlen
|
||||
req.AddData(msg)
|
||||
|
||||
rtaDst := nl.NewRtAttr(syscall.RTA_DST, destinationData)
|
||||
req.AddData(rtaDst)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
native := nl.NativeEndian()
|
||||
var res []Route
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeRtMsg(m)
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
route := Route{}
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case syscall.RTA_GATEWAY:
|
||||
route.Gw = net.IP(attr.Value)
|
||||
case syscall.RTA_PREFSRC:
|
||||
route.Src = net.IP(attr.Value)
|
||||
case syscall.RTA_DST:
|
||||
route.Dst = &net.IPNet{
|
||||
IP: attr.Value,
|
||||
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
|
||||
}
|
||||
case syscall.RTA_OIF:
|
||||
routeIndex := int(native.Uint32(attr.Value[0:4]))
|
||||
route.LinkIndex = routeIndex
|
||||
}
|
||||
}
|
||||
res = append(res, route)
|
||||
}
|
||||
return res, nil
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Proto is an enum representing an ipsec protocol.
|
||||
type Proto uint8
|
||||
|
||||
const (
|
||||
XFRM_PROTO_ROUTE2 Proto = syscall.IPPROTO_ROUTING
|
||||
XFRM_PROTO_ESP Proto = syscall.IPPROTO_ESP
|
||||
XFRM_PROTO_AH Proto = syscall.IPPROTO_AH
|
||||
XFRM_PROTO_HAO Proto = syscall.IPPROTO_DSTOPTS
|
||||
XFRM_PROTO_COMP Proto = syscall.IPPROTO_COMP
|
||||
XFRM_PROTO_IPSEC_ANY Proto = syscall.IPPROTO_RAW
|
||||
)
|
||||
|
||||
func (p Proto) String() string {
|
||||
switch p {
|
||||
case XFRM_PROTO_ROUTE2:
|
||||
return "route2"
|
||||
case XFRM_PROTO_ESP:
|
||||
return "esp"
|
||||
case XFRM_PROTO_AH:
|
||||
return "ah"
|
||||
case XFRM_PROTO_HAO:
|
||||
return "hao"
|
||||
case XFRM_PROTO_COMP:
|
||||
return "comp"
|
||||
case XFRM_PROTO_IPSEC_ANY:
|
||||
return "ipsec-any"
|
||||
}
|
||||
return fmt.Sprintf("%d", p)
|
||||
}
|
||||
|
||||
// Mode is an enum representing an ipsec transport.
|
||||
type Mode uint8
|
||||
|
||||
const (
|
||||
XFRM_MODE_TRANSPORT Mode = iota
|
||||
XFRM_MODE_TUNNEL
|
||||
XFRM_MODE_ROUTEOPTIMIZATION
|
||||
XFRM_MODE_IN_TRIGGER
|
||||
XFRM_MODE_BEET
|
||||
XFRM_MODE_MAX
|
||||
)
|
||||
|
||||
func (m Mode) String() string {
|
||||
switch m {
|
||||
case XFRM_MODE_TRANSPORT:
|
||||
return "transport"
|
||||
case XFRM_MODE_TUNNEL:
|
||||
return "tunnel"
|
||||
case XFRM_MODE_ROUTEOPTIMIZATION:
|
||||
return "ro"
|
||||
case XFRM_MODE_IN_TRIGGER:
|
||||
return "in_trigger"
|
||||
case XFRM_MODE_BEET:
|
||||
return "beet"
|
||||
}
|
||||
return fmt.Sprintf("%d", m)
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Dir is an enum representing an ipsec template direction.
|
||||
type Dir uint8
|
||||
|
||||
const (
|
||||
XFRM_DIR_IN Dir = iota
|
||||
XFRM_DIR_OUT
|
||||
XFRM_DIR_FWD
|
||||
XFRM_SOCKET_IN
|
||||
XFRM_SOCKET_OUT
|
||||
XFRM_SOCKET_FWD
|
||||
)
|
||||
|
||||
func (d Dir) String() string {
|
||||
switch d {
|
||||
case XFRM_DIR_IN:
|
||||
return "dir in"
|
||||
case XFRM_DIR_OUT:
|
||||
return "dir out"
|
||||
case XFRM_DIR_FWD:
|
||||
return "dir fwd"
|
||||
case XFRM_SOCKET_IN:
|
||||
return "socket in"
|
||||
case XFRM_SOCKET_OUT:
|
||||
return "socket out"
|
||||
case XFRM_SOCKET_FWD:
|
||||
return "socket fwd"
|
||||
}
|
||||
return fmt.Sprintf("socket %d", d-XFRM_SOCKET_IN)
|
||||
}
|
||||
|
||||
// XfrmPolicyTmpl encapsulates a rule for the base addresses of an ipsec
|
||||
// policy. These rules are matched with XfrmState to determine encryption
|
||||
// and authentication algorithms.
|
||||
type XfrmPolicyTmpl struct {
|
||||
Dst net.IP
|
||||
Src net.IP
|
||||
Proto Proto
|
||||
Mode Mode
|
||||
Reqid int
|
||||
}
|
||||
|
||||
// XfrmPolicy represents an ipsec policy. It represents the overlay network
|
||||
// and has a list of XfrmPolicyTmpls representing the base addresses of
|
||||
// the policy.
|
||||
type XfrmPolicy struct {
|
||||
Dst *net.IPNet
|
||||
Src *net.IPNet
|
||||
Dir Dir
|
||||
Priority int
|
||||
Index int
|
||||
Tmpls []XfrmPolicyTmpl
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) {
|
||||
sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP))
|
||||
sel.Daddr.FromIP(policy.Dst.IP)
|
||||
sel.Saddr.FromIP(policy.Src.IP)
|
||||
prefixlenD, _ := policy.Dst.Mask.Size()
|
||||
sel.PrefixlenD = uint8(prefixlenD)
|
||||
prefixlenS, _ := policy.Src.Mask.Size()
|
||||
sel.PrefixlenS = uint8(prefixlenS)
|
||||
}
|
||||
|
||||
// XfrmPolicyAdd will add an xfrm policy to the system.
|
||||
// Equivalent to: `ip xfrm policy add $policy`
|
||||
func XfrmPolicyAdd(policy *XfrmPolicy) error {
|
||||
req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWPOLICY, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := &nl.XfrmUserpolicyInfo{}
|
||||
selFromPolicy(&msg.Sel, policy)
|
||||
msg.Priority = uint32(policy.Priority)
|
||||
msg.Index = uint32(policy.Index)
|
||||
msg.Dir = uint8(policy.Dir)
|
||||
msg.Lft.SoftByteLimit = nl.XFRM_INF
|
||||
msg.Lft.HardByteLimit = nl.XFRM_INF
|
||||
msg.Lft.SoftPacketLimit = nl.XFRM_INF
|
||||
msg.Lft.HardPacketLimit = nl.XFRM_INF
|
||||
req.AddData(msg)
|
||||
|
||||
tmplData := make([]byte, nl.SizeofXfrmUserTmpl*len(policy.Tmpls))
|
||||
for i, tmpl := range policy.Tmpls {
|
||||
start := i * nl.SizeofXfrmUserTmpl
|
||||
userTmpl := nl.DeserializeXfrmUserTmpl(tmplData[start : start+nl.SizeofXfrmUserTmpl])
|
||||
userTmpl.XfrmId.Daddr.FromIP(tmpl.Dst)
|
||||
userTmpl.Saddr.FromIP(tmpl.Src)
|
||||
userTmpl.XfrmId.Proto = uint8(tmpl.Proto)
|
||||
userTmpl.Mode = uint8(tmpl.Mode)
|
||||
userTmpl.Reqid = uint32(tmpl.Reqid)
|
||||
userTmpl.Aalgos = ^uint32(0)
|
||||
userTmpl.Ealgos = ^uint32(0)
|
||||
userTmpl.Calgos = ^uint32(0)
|
||||
}
|
||||
if len(tmplData) > 0 {
|
||||
tmpls := nl.NewRtAttr(nl.XFRMA_TMPL, tmplData)
|
||||
req.AddData(tmpls)
|
||||
}
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// XfrmPolicyDel will delete an xfrm policy from the system. Note that
|
||||
// the Tmpls are ignored when matching the policy to delete.
|
||||
// Equivalent to: `ip xfrm policy del $policy`
|
||||
func XfrmPolicyDel(policy *XfrmPolicy) error {
|
||||
req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELPOLICY, syscall.NLM_F_ACK)
|
||||
|
||||
msg := &nl.XfrmUserpolicyId{}
|
||||
selFromPolicy(&msg.Sel, policy)
|
||||
msg.Index = uint32(policy.Index)
|
||||
msg.Dir = uint8(policy.Dir)
|
||||
req.AddData(msg)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// XfrmPolicyList gets a list of xfrm policies in the system.
|
||||
// Equivalent to: `ip xfrm policy show`.
|
||||
// The list can be filtered by ip family.
|
||||
func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
|
||||
req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETPOLICY, syscall.NLM_F_DUMP)
|
||||
|
||||
msg := nl.NewIfInfomsg(family)
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []XfrmPolicy
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeXfrmUserpolicyInfo(m)
|
||||
|
||||
if family != FAMILY_ALL && family != int(msg.Sel.Family) {
|
||||
continue
|
||||
}
|
||||
|
||||
var policy XfrmPolicy
|
||||
|
||||
policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD)
|
||||
policy.Src = msg.Sel.Saddr.ToIPNet(msg.Sel.PrefixlenS)
|
||||
policy.Priority = int(msg.Priority)
|
||||
policy.Index = int(msg.Index)
|
||||
policy.Dir = Dir(msg.Dir)
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case nl.XFRMA_TMPL:
|
||||
max := len(attr.Value)
|
||||
for i := 0; i < max; i += nl.SizeofXfrmUserTmpl {
|
||||
var resTmpl XfrmPolicyTmpl
|
||||
tmpl := nl.DeserializeXfrmUserTmpl(attr.Value[i : i+nl.SizeofXfrmUserTmpl])
|
||||
resTmpl.Dst = tmpl.XfrmId.Daddr.ToIP()
|
||||
resTmpl.Src = tmpl.Saddr.ToIP()
|
||||
resTmpl.Proto = Proto(tmpl.XfrmId.Proto)
|
||||
resTmpl.Mode = Mode(tmpl.Mode)
|
||||
resTmpl.Reqid = int(tmpl.Reqid)
|
||||
policy.Tmpls = append(policy.Tmpls, resTmpl)
|
||||
}
|
||||
}
|
||||
}
|
||||
res = append(res, policy)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// XfrmStateAlgo represents the algorithm to use for the ipsec encryption.
|
||||
type XfrmStateAlgo struct {
|
||||
Name string
|
||||
Key []byte
|
||||
TruncateLen int // Auth only
|
||||
}
|
||||
|
||||
// EncapType is an enum representing an ipsec template direction.
|
||||
type EncapType uint8
|
||||
|
||||
const (
|
||||
XFRM_ENCAP_ESPINUDP_NONIKE EncapType = iota + 1
|
||||
XFRM_ENCAP_ESPINUDP
|
||||
)
|
||||
|
||||
func (e EncapType) String() string {
|
||||
switch e {
|
||||
case XFRM_ENCAP_ESPINUDP_NONIKE:
|
||||
return "espinudp-nonike"
|
||||
case XFRM_ENCAP_ESPINUDP:
|
||||
return "espinudp"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// XfrmEncap represents the encapsulation to use for the ipsec encryption.
|
||||
type XfrmStateEncap struct {
|
||||
Type EncapType
|
||||
SrcPort int
|
||||
DstPort int
|
||||
OriginalAddress net.IP
|
||||
}
|
||||
|
||||
// XfrmState represents the state of an ipsec policy. It optionally
|
||||
// contains an XfrmStateAlgo for encryption and one for authentication.
|
||||
type XfrmState struct {
|
||||
Dst net.IP
|
||||
Src net.IP
|
||||
Proto Proto
|
||||
Mode Mode
|
||||
Spi int
|
||||
Reqid int
|
||||
ReplayWindow int
|
||||
Auth *XfrmStateAlgo
|
||||
Crypt *XfrmStateAlgo
|
||||
Encap *XfrmStateEncap
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
func writeStateAlgo(a *XfrmStateAlgo) []byte {
|
||||
algo := nl.XfrmAlgo{
|
||||
AlgKeyLen: uint32(len(a.Key) * 8),
|
||||
AlgKey: a.Key,
|
||||
}
|
||||
end := len(a.Name)
|
||||
if end > 64 {
|
||||
end = 64
|
||||
}
|
||||
copy(algo.AlgName[:end], a.Name)
|
||||
return algo.Serialize()
|
||||
}
|
||||
|
||||
func writeStateAlgoAuth(a *XfrmStateAlgo) []byte {
|
||||
algo := nl.XfrmAlgoAuth{
|
||||
AlgKeyLen: uint32(len(a.Key) * 8),
|
||||
AlgTruncLen: uint32(a.TruncateLen),
|
||||
AlgKey: a.Key,
|
||||
}
|
||||
end := len(a.Name)
|
||||
if end > 64 {
|
||||
end = 64
|
||||
}
|
||||
copy(algo.AlgName[:end], a.Name)
|
||||
return algo.Serialize()
|
||||
}
|
||||
|
||||
// XfrmStateAdd will add an xfrm state to the system.
|
||||
// Equivalent to: `ip xfrm state add $state`
|
||||
func XfrmStateAdd(state *XfrmState) error {
|
||||
// A state with spi 0 can't be deleted so don't allow it to be set
|
||||
if state.Spi == 0 {
|
||||
return fmt.Errorf("Spi must be set when adding xfrm state.")
|
||||
}
|
||||
req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWSA, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := &nl.XfrmUsersaInfo{}
|
||||
msg.Family = uint16(nl.GetIPFamily(state.Dst))
|
||||
msg.Id.Daddr.FromIP(state.Dst)
|
||||
msg.Saddr.FromIP(state.Src)
|
||||
msg.Id.Proto = uint8(state.Proto)
|
||||
msg.Mode = uint8(state.Mode)
|
||||
msg.Id.Spi = nl.Swap32(uint32(state.Spi))
|
||||
msg.Reqid = uint32(state.Reqid)
|
||||
msg.ReplayWindow = uint8(state.ReplayWindow)
|
||||
msg.Lft.SoftByteLimit = nl.XFRM_INF
|
||||
msg.Lft.HardByteLimit = nl.XFRM_INF
|
||||
msg.Lft.SoftPacketLimit = nl.XFRM_INF
|
||||
msg.Lft.HardPacketLimit = nl.XFRM_INF
|
||||
req.AddData(msg)
|
||||
|
||||
if state.Auth != nil {
|
||||
out := nl.NewRtAttr(nl.XFRMA_ALG_AUTH_TRUNC, writeStateAlgoAuth(state.Auth))
|
||||
req.AddData(out)
|
||||
}
|
||||
if state.Crypt != nil {
|
||||
out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt))
|
||||
req.AddData(out)
|
||||
}
|
||||
if state.Encap != nil {
|
||||
encapData := make([]byte, nl.SizeofXfrmEncapTmpl)
|
||||
encap := nl.DeserializeXfrmEncapTmpl(encapData)
|
||||
encap.EncapType = uint16(state.Encap.Type)
|
||||
encap.EncapSport = nl.Swap16(uint16(state.Encap.SrcPort))
|
||||
encap.EncapDport = nl.Swap16(uint16(state.Encap.DstPort))
|
||||
encap.EncapOa.FromIP(state.Encap.OriginalAddress)
|
||||
out := nl.NewRtAttr(nl.XFRMA_ENCAP, encapData)
|
||||
req.AddData(out)
|
||||
}
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// XfrmStateDel will delete an xfrm state from the system. Note that
|
||||
// the Algos are ignored when matching the state to delete.
|
||||
// Equivalent to: `ip xfrm state del $state`
|
||||
func XfrmStateDel(state *XfrmState) error {
|
||||
req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELSA, syscall.NLM_F_ACK)
|
||||
|
||||
msg := &nl.XfrmUsersaId{}
|
||||
msg.Daddr.FromIP(state.Dst)
|
||||
msg.Family = uint16(nl.GetIPFamily(state.Dst))
|
||||
msg.Proto = uint8(state.Proto)
|
||||
msg.Spi = nl.Swap32(uint32(state.Spi))
|
||||
req.AddData(msg)
|
||||
|
||||
saddr := nl.XfrmAddress{}
|
||||
saddr.FromIP(state.Src)
|
||||
srcdata := nl.NewRtAttr(nl.XFRMA_SRCADDR, saddr.Serialize())
|
||||
|
||||
req.AddData(srcdata)
|
||||
|
||||
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// XfrmStateList gets a list of xfrm states in the system.
|
||||
// Equivalent to: `ip xfrm state show`.
|
||||
// The list can be filtered by ip family.
|
||||
func XfrmStateList(family int) ([]XfrmState, error) {
|
||||
req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP)
|
||||
|
||||
msg := nl.NewIfInfomsg(family)
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []XfrmState
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeXfrmUsersaInfo(m)
|
||||
|
||||
if family != FAMILY_ALL && family != int(msg.Family) {
|
||||
continue
|
||||
}
|
||||
|
||||
var state XfrmState
|
||||
|
||||
state.Dst = msg.Id.Daddr.ToIP()
|
||||
state.Src = msg.Saddr.ToIP()
|
||||
state.Proto = Proto(msg.Id.Proto)
|
||||
state.Mode = Mode(msg.Mode)
|
||||
state.Spi = int(nl.Swap32(msg.Id.Spi))
|
||||
state.Reqid = int(msg.Reqid)
|
||||
state.ReplayWindow = int(msg.ReplayWindow)
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case nl.XFRMA_ALG_AUTH, nl.XFRMA_ALG_CRYPT:
|
||||
var resAlgo *XfrmStateAlgo
|
||||
if attr.Attr.Type == nl.XFRMA_ALG_AUTH {
|
||||
if state.Auth == nil {
|
||||
state.Auth = new(XfrmStateAlgo)
|
||||
}
|
||||
resAlgo = state.Auth
|
||||
} else {
|
||||
state.Crypt = new(XfrmStateAlgo)
|
||||
resAlgo = state.Crypt
|
||||
}
|
||||
algo := nl.DeserializeXfrmAlgo(attr.Value[:])
|
||||
(*resAlgo).Name = nl.BytesToString(algo.AlgName[:])
|
||||
(*resAlgo).Key = algo.AlgKey
|
||||
case nl.XFRMA_ALG_AUTH_TRUNC:
|
||||
if state.Auth == nil {
|
||||
state.Auth = new(XfrmStateAlgo)
|
||||
}
|
||||
algo := nl.DeserializeXfrmAlgoAuth(attr.Value[:])
|
||||
state.Auth.Name = nl.BytesToString(algo.AlgName[:])
|
||||
state.Auth.Key = algo.AlgKey
|
||||
state.Auth.TruncateLen = int(algo.AlgTruncLen)
|
||||
case nl.XFRMA_ENCAP:
|
||||
encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:])
|
||||
state.Encap = new(XfrmStateEncap)
|
||||
state.Encap.Type = EncapType(encap.EncapType)
|
||||
state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport))
|
||||
state.Encap.DstPort = int(nl.Swap16(encap.EncapDport))
|
||||
state.Encap.OriginalAddress = encap.EncapOa.ToIP()
|
||||
}
|
||||
|
||||
}
|
||||
res = append(res, state)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
Reference in New Issue
Block a user