Update dependencies

This commit is contained in:
bluepython508
2024-11-01 17:33:34 +00:00
parent 033ac0b400
commit 5cdfab398d
3596 changed files with 1033483 additions and 259 deletions

202
vendor/github.com/google/btree/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
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
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

10
vendor/github.com/google/btree/README.md generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# BTree implementation for Go
This package provides an in-memory B-Tree implementation for Go, useful as
an ordered, mutable data structure.
The API is based off of the wonderful
http://godoc.org/github.com/petar/GoLLRB/llrb, and is meant to allow btree to
act as a drop-in replacement for gollrb trees.
See http://godoc.org/github.com/google/btree for documentation.

893
vendor/github.com/google/btree/btree.go generated vendored Normal file
View File

@@ -0,0 +1,893 @@
// Copyright 2014 Google 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.
//go:build !go1.18
// +build !go1.18
// Package btree implements in-memory B-Trees of arbitrary degree.
//
// btree implements an in-memory B-Tree for use as an ordered data structure.
// It is not meant for persistent storage solutions.
//
// It has a flatter structure than an equivalent red-black or other binary tree,
// which in some cases yields better memory usage and/or performance.
// See some discussion on the matter here:
// http://google-opensource.blogspot.com/2013/01/c-containers-that-save-memory-and-time.html
// Note, though, that this project is in no way related to the C++ B-Tree
// implementation written about there.
//
// Within this tree, each node contains a slice of items and a (possibly nil)
// slice of children. For basic numeric values or raw structs, this can cause
// efficiency differences when compared to equivalent C++ template code that
// stores values in arrays within the node:
// * Due to the overhead of storing values as interfaces (each
// value needs to be stored as the value itself, then 2 words for the
// interface pointing to that value and its type), resulting in higher
// memory use.
// * Since interfaces can point to values anywhere in memory, values are
// most likely not stored in contiguous blocks, resulting in a higher
// number of cache misses.
// These issues don't tend to matter, though, when working with strings or other
// heap-allocated structures, since C++-equivalent structures also must store
// pointers and also distribute their values across the heap.
//
// This implementation is designed to be a drop-in replacement to gollrb.LLRB
// trees, (http://github.com/petar/gollrb), an excellent and probably the most
// widely used ordered tree implementation in the Go ecosystem currently.
// Its functions, therefore, exactly mirror those of
// llrb.LLRB where possible. Unlike gollrb, though, we currently don't
// support storing multiple equivalent values.
package btree
import (
"fmt"
"io"
"sort"
"strings"
"sync"
)
// Item represents a single object in the tree.
type Item interface {
// Less tests whether the current item is less than the given argument.
//
// This must provide a strict weak ordering.
// If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only
// hold one of either a or b in the tree).
Less(than Item) bool
}
const (
DefaultFreeListSize = 32
)
var (
nilItems = make(items, 16)
nilChildren = make(children, 16)
)
// FreeList represents a free list of btree nodes. By default each
// BTree has its own FreeList, but multiple BTrees can share the same
// FreeList.
// Two Btrees using the same freelist are safe for concurrent write access.
type FreeList struct {
mu sync.Mutex
freelist []*node
}
// NewFreeList creates a new free list.
// size is the maximum size of the returned free list.
func NewFreeList(size int) *FreeList {
return &FreeList{freelist: make([]*node, 0, size)}
}
func (f *FreeList) newNode() (n *node) {
f.mu.Lock()
index := len(f.freelist) - 1
if index < 0 {
f.mu.Unlock()
return new(node)
}
n = f.freelist[index]
f.freelist[index] = nil
f.freelist = f.freelist[:index]
f.mu.Unlock()
return
}
// freeNode adds the given node to the list, returning true if it was added
// and false if it was discarded.
func (f *FreeList) freeNode(n *node) (out bool) {
f.mu.Lock()
if len(f.freelist) < cap(f.freelist) {
f.freelist = append(f.freelist, n)
out = true
}
f.mu.Unlock()
return
}
// ItemIterator allows callers of Ascend* to iterate in-order over portions of
// the tree. When this function returns false, iteration will stop and the
// associated Ascend* function will immediately return.
type ItemIterator func(i Item) bool
// New creates a new B-Tree with the given degree.
//
// New(2), for example, will create a 2-3-4 tree (each node contains 1-3 items
// and 2-4 children).
func New(degree int) *BTree {
return NewWithFreeList(degree, NewFreeList(DefaultFreeListSize))
}
// NewWithFreeList creates a new B-Tree that uses the given node free list.
func NewWithFreeList(degree int, f *FreeList) *BTree {
if degree <= 1 {
panic("bad degree")
}
return &BTree{
degree: degree,
cow: &copyOnWriteContext{freelist: f},
}
}
// items stores items in a node.
type items []Item
// insertAt inserts a value into the given index, pushing all subsequent values
// forward.
func (s *items) insertAt(index int, item Item) {
*s = append(*s, nil)
if index < len(*s) {
copy((*s)[index+1:], (*s)[index:])
}
(*s)[index] = item
}
// removeAt removes a value at a given index, pulling all subsequent values
// back.
func (s *items) removeAt(index int) Item {
item := (*s)[index]
copy((*s)[index:], (*s)[index+1:])
(*s)[len(*s)-1] = nil
*s = (*s)[:len(*s)-1]
return item
}
// pop removes and returns the last element in the list.
func (s *items) pop() (out Item) {
index := len(*s) - 1
out = (*s)[index]
(*s)[index] = nil
*s = (*s)[:index]
return
}
// truncate truncates this instance at index so that it contains only the
// first index items. index must be less than or equal to length.
func (s *items) truncate(index int) {
var toClear items
*s, toClear = (*s)[:index], (*s)[index:]
for len(toClear) > 0 {
toClear = toClear[copy(toClear, nilItems):]
}
}
// find returns the index where the given item should be inserted into this
// list. 'found' is true if the item already exists in the list at the given
// index.
func (s items) find(item Item) (index int, found bool) {
i := sort.Search(len(s), func(i int) bool {
return item.Less(s[i])
})
if i > 0 && !s[i-1].Less(item) {
return i - 1, true
}
return i, false
}
// children stores child nodes in a node.
type children []*node
// insertAt inserts a value into the given index, pushing all subsequent values
// forward.
func (s *children) insertAt(index int, n *node) {
*s = append(*s, nil)
if index < len(*s) {
copy((*s)[index+1:], (*s)[index:])
}
(*s)[index] = n
}
// removeAt removes a value at a given index, pulling all subsequent values
// back.
func (s *children) removeAt(index int) *node {
n := (*s)[index]
copy((*s)[index:], (*s)[index+1:])
(*s)[len(*s)-1] = nil
*s = (*s)[:len(*s)-1]
return n
}
// pop removes and returns the last element in the list.
func (s *children) pop() (out *node) {
index := len(*s) - 1
out = (*s)[index]
(*s)[index] = nil
*s = (*s)[:index]
return
}
// truncate truncates this instance at index so that it contains only the
// first index children. index must be less than or equal to length.
func (s *children) truncate(index int) {
var toClear children
*s, toClear = (*s)[:index], (*s)[index:]
for len(toClear) > 0 {
toClear = toClear[copy(toClear, nilChildren):]
}
}
// node is an internal node in a tree.
//
// It must at all times maintain the invariant that either
// * len(children) == 0, len(items) unconstrained
// * len(children) == len(items) + 1
type node struct {
items items
children children
cow *copyOnWriteContext
}
func (n *node) mutableFor(cow *copyOnWriteContext) *node {
if n.cow == cow {
return n
}
out := cow.newNode()
if cap(out.items) >= len(n.items) {
out.items = out.items[:len(n.items)]
} else {
out.items = make(items, len(n.items), cap(n.items))
}
copy(out.items, n.items)
// Copy children
if cap(out.children) >= len(n.children) {
out.children = out.children[:len(n.children)]
} else {
out.children = make(children, len(n.children), cap(n.children))
}
copy(out.children, n.children)
return out
}
func (n *node) mutableChild(i int) *node {
c := n.children[i].mutableFor(n.cow)
n.children[i] = c
return c
}
// split splits the given node at the given index. The current node shrinks,
// and this function returns the item that existed at that index and a new node
// containing all items/children after it.
func (n *node) split(i int) (Item, *node) {
item := n.items[i]
next := n.cow.newNode()
next.items = append(next.items, n.items[i+1:]...)
n.items.truncate(i)
if len(n.children) > 0 {
next.children = append(next.children, n.children[i+1:]...)
n.children.truncate(i + 1)
}
return item, next
}
// maybeSplitChild checks if a child should be split, and if so splits it.
// Returns whether or not a split occurred.
func (n *node) maybeSplitChild(i, maxItems int) bool {
if len(n.children[i].items) < maxItems {
return false
}
first := n.mutableChild(i)
item, second := first.split(maxItems / 2)
n.items.insertAt(i, item)
n.children.insertAt(i+1, second)
return true
}
// insert inserts an item into the subtree rooted at this node, making sure
// no nodes in the subtree exceed maxItems items. Should an equivalent item be
// be found/replaced by insert, it will be returned.
func (n *node) insert(item Item, maxItems int) Item {
i, found := n.items.find(item)
if found {
out := n.items[i]
n.items[i] = item
return out
}
if len(n.children) == 0 {
n.items.insertAt(i, item)
return nil
}
if n.maybeSplitChild(i, maxItems) {
inTree := n.items[i]
switch {
case item.Less(inTree):
// no change, we want first split node
case inTree.Less(item):
i++ // we want second split node
default:
out := n.items[i]
n.items[i] = item
return out
}
}
return n.mutableChild(i).insert(item, maxItems)
}
// get finds the given key in the subtree and returns it.
func (n *node) get(key Item) Item {
i, found := n.items.find(key)
if found {
return n.items[i]
} else if len(n.children) > 0 {
return n.children[i].get(key)
}
return nil
}
// min returns the first item in the subtree.
func min(n *node) Item {
if n == nil {
return nil
}
for len(n.children) > 0 {
n = n.children[0]
}
if len(n.items) == 0 {
return nil
}
return n.items[0]
}
// max returns the last item in the subtree.
func max(n *node) Item {
if n == nil {
return nil
}
for len(n.children) > 0 {
n = n.children[len(n.children)-1]
}
if len(n.items) == 0 {
return nil
}
return n.items[len(n.items)-1]
}
// toRemove details what item to remove in a node.remove call.
type toRemove int
const (
removeItem toRemove = iota // removes the given item
removeMin // removes smallest item in the subtree
removeMax // removes largest item in the subtree
)
// remove removes an item from the subtree rooted at this node.
func (n *node) remove(item Item, minItems int, typ toRemove) Item {
var i int
var found bool
switch typ {
case removeMax:
if len(n.children) == 0 {
return n.items.pop()
}
i = len(n.items)
case removeMin:
if len(n.children) == 0 {
return n.items.removeAt(0)
}
i = 0
case removeItem:
i, found = n.items.find(item)
if len(n.children) == 0 {
if found {
return n.items.removeAt(i)
}
return nil
}
default:
panic("invalid type")
}
// If we get to here, we have children.
if len(n.children[i].items) <= minItems {
return n.growChildAndRemove(i, item, minItems, typ)
}
child := n.mutableChild(i)
// Either we had enough items to begin with, or we've done some
// merging/stealing, because we've got enough now and we're ready to return
// stuff.
if found {
// The item exists at index 'i', and the child we've selected can give us a
// predecessor, since if we've gotten here it's got > minItems items in it.
out := n.items[i]
// We use our special-case 'remove' call with typ=maxItem to pull the
// predecessor of item i (the rightmost leaf of our immediate left child)
// and set it into where we pulled the item from.
n.items[i] = child.remove(nil, minItems, removeMax)
return out
}
// Final recursive call. Once we're here, we know that the item isn't in this
// node and that the child is big enough to remove from.
return child.remove(item, minItems, typ)
}
// growChildAndRemove grows child 'i' to make sure it's possible to remove an
// item from it while keeping it at minItems, then calls remove to actually
// remove it.
//
// Most documentation says we have to do two sets of special casing:
// 1) item is in this node
// 2) item is in child
// In both cases, we need to handle the two subcases:
// A) node has enough values that it can spare one
// B) node doesn't have enough values
// For the latter, we have to check:
// a) left sibling has node to spare
// b) right sibling has node to spare
// c) we must merge
// To simplify our code here, we handle cases #1 and #2 the same:
// If a node doesn't have enough items, we make sure it does (using a,b,c).
// We then simply redo our remove call, and the second time (regardless of
// whether we're in case 1 or 2), we'll have enough items and can guarantee
// that we hit case A.
func (n *node) growChildAndRemove(i int, item Item, minItems int, typ toRemove) Item {
if i > 0 && len(n.children[i-1].items) > minItems {
// Steal from left child
child := n.mutableChild(i)
stealFrom := n.mutableChild(i - 1)
stolenItem := stealFrom.items.pop()
child.items.insertAt(0, n.items[i-1])
n.items[i-1] = stolenItem
if len(stealFrom.children) > 0 {
child.children.insertAt(0, stealFrom.children.pop())
}
} else if i < len(n.items) && len(n.children[i+1].items) > minItems {
// steal from right child
child := n.mutableChild(i)
stealFrom := n.mutableChild(i + 1)
stolenItem := stealFrom.items.removeAt(0)
child.items = append(child.items, n.items[i])
n.items[i] = stolenItem
if len(stealFrom.children) > 0 {
child.children = append(child.children, stealFrom.children.removeAt(0))
}
} else {
if i >= len(n.items) {
i--
}
child := n.mutableChild(i)
// merge with right child
mergeItem := n.items.removeAt(i)
mergeChild := n.children.removeAt(i + 1)
child.items = append(child.items, mergeItem)
child.items = append(child.items, mergeChild.items...)
child.children = append(child.children, mergeChild.children...)
n.cow.freeNode(mergeChild)
}
return n.remove(item, minItems, typ)
}
type direction int
const (
descend = direction(-1)
ascend = direction(+1)
)
// iterate provides a simple method for iterating over elements in the tree.
//
// When ascending, the 'start' should be less than 'stop' and when descending,
// the 'start' should be greater than 'stop'. Setting 'includeStart' to true
// will force the iterator to include the first item when it equals 'start',
// thus creating a "greaterOrEqual" or "lessThanEqual" rather than just a
// "greaterThan" or "lessThan" queries.
func (n *node) iterate(dir direction, start, stop Item, includeStart bool, hit bool, iter ItemIterator) (bool, bool) {
var ok, found bool
var index int
switch dir {
case ascend:
if start != nil {
index, _ = n.items.find(start)
}
for i := index; i < len(n.items); i++ {
if len(n.children) > 0 {
if hit, ok = n.children[i].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
if !includeStart && !hit && start != nil && !start.Less(n.items[i]) {
hit = true
continue
}
hit = true
if stop != nil && !n.items[i].Less(stop) {
return hit, false
}
if !iter(n.items[i]) {
return hit, false
}
}
if len(n.children) > 0 {
if hit, ok = n.children[len(n.children)-1].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
case descend:
if start != nil {
index, found = n.items.find(start)
if !found {
index = index - 1
}
} else {
index = len(n.items) - 1
}
for i := index; i >= 0; i-- {
if start != nil && !n.items[i].Less(start) {
if !includeStart || hit || start.Less(n.items[i]) {
continue
}
}
if len(n.children) > 0 {
if hit, ok = n.children[i+1].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
if stop != nil && !stop.Less(n.items[i]) {
return hit, false // continue
}
hit = true
if !iter(n.items[i]) {
return hit, false
}
}
if len(n.children) > 0 {
if hit, ok = n.children[0].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
}
return hit, true
}
// Used for testing/debugging purposes.
func (n *node) print(w io.Writer, level int) {
fmt.Fprintf(w, "%sNODE:%v\n", strings.Repeat(" ", level), n.items)
for _, c := range n.children {
c.print(w, level+1)
}
}
// BTree is an implementation of a B-Tree.
//
// BTree stores Item instances in an ordered structure, allowing easy insertion,
// removal, and iteration.
//
// Write operations are not safe for concurrent mutation by multiple
// goroutines, but Read operations are.
type BTree struct {
degree int
length int
root *node
cow *copyOnWriteContext
}
// copyOnWriteContext pointers determine node ownership... a tree with a write
// context equivalent to a node's write context is allowed to modify that node.
// A tree whose write context does not match a node's is not allowed to modify
// it, and must create a new, writable copy (IE: it's a Clone).
//
// When doing any write operation, we maintain the invariant that the current
// node's context is equal to the context of the tree that requested the write.
// We do this by, before we descend into any node, creating a copy with the
// correct context if the contexts don't match.
//
// Since the node we're currently visiting on any write has the requesting
// tree's context, that node is modifiable in place. Children of that node may
// not share context, but before we descend into them, we'll make a mutable
// copy.
type copyOnWriteContext struct {
freelist *FreeList
}
// Clone clones the btree, lazily. Clone should not be called concurrently,
// but the original tree (t) and the new tree (t2) can be used concurrently
// once the Clone call completes.
//
// The internal tree structure of b is marked read-only and shared between t and
// t2. Writes to both t and t2 use copy-on-write logic, creating new nodes
// whenever one of b's original nodes would have been modified. Read operations
// should have no performance degredation. Write operations for both t and t2
// will initially experience minor slow-downs caused by additional allocs and
// copies due to the aforementioned copy-on-write logic, but should converge to
// the original performance characteristics of the original tree.
func (t *BTree) Clone() (t2 *BTree) {
// Create two entirely new copy-on-write contexts.
// This operation effectively creates three trees:
// the original, shared nodes (old b.cow)
// the new b.cow nodes
// the new out.cow nodes
cow1, cow2 := *t.cow, *t.cow
out := *t
t.cow = &cow1
out.cow = &cow2
return &out
}
// maxItems returns the max number of items to allow per node.
func (t *BTree) maxItems() int {
return t.degree*2 - 1
}
// minItems returns the min number of items to allow per node (ignored for the
// root node).
func (t *BTree) minItems() int {
return t.degree - 1
}
func (c *copyOnWriteContext) newNode() (n *node) {
n = c.freelist.newNode()
n.cow = c
return
}
type freeType int
const (
ftFreelistFull freeType = iota // node was freed (available for GC, not stored in freelist)
ftStored // node was stored in the freelist for later use
ftNotOwned // node was ignored by COW, since it's owned by another one
)
// freeNode frees a node within a given COW context, if it's owned by that
// context. It returns what happened to the node (see freeType const
// documentation).
func (c *copyOnWriteContext) freeNode(n *node) freeType {
if n.cow == c {
// clear to allow GC
n.items.truncate(0)
n.children.truncate(0)
n.cow = nil
if c.freelist.freeNode(n) {
return ftStored
} else {
return ftFreelistFull
}
} else {
return ftNotOwned
}
}
// ReplaceOrInsert adds the given item to the tree. If an item in the tree
// already equals the given one, it is removed from the tree and returned.
// Otherwise, nil is returned.
//
// nil cannot be added to the tree (will panic).
func (t *BTree) ReplaceOrInsert(item Item) Item {
if item == nil {
panic("nil item being added to BTree")
}
if t.root == nil {
t.root = t.cow.newNode()
t.root.items = append(t.root.items, item)
t.length++
return nil
} else {
t.root = t.root.mutableFor(t.cow)
if len(t.root.items) >= t.maxItems() {
item2, second := t.root.split(t.maxItems() / 2)
oldroot := t.root
t.root = t.cow.newNode()
t.root.items = append(t.root.items, item2)
t.root.children = append(t.root.children, oldroot, second)
}
}
out := t.root.insert(item, t.maxItems())
if out == nil {
t.length++
}
return out
}
// Delete removes an item equal to the passed in item from the tree, returning
// it. If no such item exists, returns nil.
func (t *BTree) Delete(item Item) Item {
return t.deleteItem(item, removeItem)
}
// DeleteMin removes the smallest item in the tree and returns it.
// If no such item exists, returns nil.
func (t *BTree) DeleteMin() Item {
return t.deleteItem(nil, removeMin)
}
// DeleteMax removes the largest item in the tree and returns it.
// If no such item exists, returns nil.
func (t *BTree) DeleteMax() Item {
return t.deleteItem(nil, removeMax)
}
func (t *BTree) deleteItem(item Item, typ toRemove) Item {
if t.root == nil || len(t.root.items) == 0 {
return nil
}
t.root = t.root.mutableFor(t.cow)
out := t.root.remove(item, t.minItems(), typ)
if len(t.root.items) == 0 && len(t.root.children) > 0 {
oldroot := t.root
t.root = t.root.children[0]
t.cow.freeNode(oldroot)
}
if out != nil {
t.length--
}
return out
}
// AscendRange calls the iterator for every value in the tree within the range
// [greaterOrEqual, lessThan), until iterator returns false.
func (t *BTree) AscendRange(greaterOrEqual, lessThan Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, greaterOrEqual, lessThan, true, false, iterator)
}
// AscendLessThan calls the iterator for every value in the tree within the range
// [first, pivot), until iterator returns false.
func (t *BTree) AscendLessThan(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, nil, pivot, false, false, iterator)
}
// AscendGreaterOrEqual calls the iterator for every value in the tree within
// the range [pivot, last], until iterator returns false.
func (t *BTree) AscendGreaterOrEqual(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, pivot, nil, true, false, iterator)
}
// Ascend calls the iterator for every value in the tree within the range
// [first, last], until iterator returns false.
func (t *BTree) Ascend(iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, nil, nil, false, false, iterator)
}
// DescendRange calls the iterator for every value in the tree within the range
// [lessOrEqual, greaterThan), until iterator returns false.
func (t *BTree) DescendRange(lessOrEqual, greaterThan Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, lessOrEqual, greaterThan, true, false, iterator)
}
// DescendLessOrEqual calls the iterator for every value in the tree within the range
// [pivot, first], until iterator returns false.
func (t *BTree) DescendLessOrEqual(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, pivot, nil, true, false, iterator)
}
// DescendGreaterThan calls the iterator for every value in the tree within
// the range [last, pivot), until iterator returns false.
func (t *BTree) DescendGreaterThan(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, nil, pivot, false, false, iterator)
}
// Descend calls the iterator for every value in the tree within the range
// [last, first], until iterator returns false.
func (t *BTree) Descend(iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, nil, nil, false, false, iterator)
}
// Get looks for the key item in the tree, returning it. It returns nil if
// unable to find that item.
func (t *BTree) Get(key Item) Item {
if t.root == nil {
return nil
}
return t.root.get(key)
}
// Min returns the smallest item in the tree, or nil if the tree is empty.
func (t *BTree) Min() Item {
return min(t.root)
}
// Max returns the largest item in the tree, or nil if the tree is empty.
func (t *BTree) Max() Item {
return max(t.root)
}
// Has returns true if the given key is in the tree.
func (t *BTree) Has(key Item) bool {
return t.Get(key) != nil
}
// Len returns the number of items currently in the tree.
func (t *BTree) Len() int {
return t.length
}
// Clear removes all items from the btree. If addNodesToFreelist is true,
// t's nodes are added to its freelist as part of this call, until the freelist
// is full. Otherwise, the root node is simply dereferenced and the subtree
// left to Go's normal GC processes.
//
// This can be much faster
// than calling Delete on all elements, because that requires finding/removing
// each element in the tree and updating the tree accordingly. It also is
// somewhat faster than creating a new tree to replace the old one, because
// nodes from the old tree are reclaimed into the freelist for use by the new
// one, instead of being lost to the garbage collector.
//
// This call takes:
// O(1): when addNodesToFreelist is false, this is a single operation.
// O(1): when the freelist is already full, it breaks out immediately
// O(freelist size): when the freelist is empty and the nodes are all owned
// by this tree, nodes are added to the freelist until full.
// O(tree size): when all nodes are owned by another tree, all nodes are
// iterated over looking for nodes to add to the freelist, and due to
// ownership, none are.
func (t *BTree) Clear(addNodesToFreelist bool) {
if t.root != nil && addNodesToFreelist {
t.root.reset(t.cow)
}
t.root, t.length = nil, 0
}
// reset returns a subtree to the freelist. It breaks out immediately if the
// freelist is full, since the only benefit of iterating is to fill that
// freelist up. Returns true if parent reset call should continue.
func (n *node) reset(c *copyOnWriteContext) bool {
for _, child := range n.children {
if !child.reset(c) {
return false
}
}
return c.freeNode(n) != ftFreelistFull
}
// Int implements the Item interface for integers.
type Int int
// Less returns true if int(a) < int(b).
func (a Int) Less(b Item) bool {
return a < b.(Int)
}

1083
vendor/github.com/google/btree/btree_generic.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

27
vendor/github.com/google/go-cmp/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2017 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

671
vendor/github.com/google/go-cmp/cmp/compare.go generated vendored Normal file
View File

@@ -0,0 +1,671 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package cmp determines equality of values.
//
// This package is intended to be a more powerful and safer alternative to
// [reflect.DeepEqual] for comparing whether two values are semantically equal.
// It is intended to only be used in tests, as performance is not a goal and
// it may panic if it cannot compare the values. Its propensity towards
// panicking means that its unsuitable for production environments where a
// spurious panic may be fatal.
//
// The primary features of cmp are:
//
// - When the default behavior of equality does not suit the test's needs,
// custom equality functions can override the equality operation.
// For example, an equality function may report floats as equal so long as
// they are within some tolerance of each other.
//
// - Types with an Equal method (e.g., [time.Time.Equal]) may use that method
// to determine equality. This allows package authors to determine
// the equality operation for the types that they define.
//
// - If no custom equality functions are used and no Equal method is defined,
// equality is determined by recursively comparing the primitive kinds on
// both values, much like [reflect.DeepEqual]. Unlike [reflect.DeepEqual],
// unexported fields are not compared by default; they result in panics
// unless suppressed by using an [Ignore] option
// (see [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported])
// or explicitly compared using the [Exporter] option.
package cmp
import (
"fmt"
"reflect"
"strings"
"github.com/google/go-cmp/cmp/internal/diff"
"github.com/google/go-cmp/cmp/internal/function"
"github.com/google/go-cmp/cmp/internal/value"
)
// TODO(≥go1.18): Use any instead of interface{}.
// Equal reports whether x and y are equal by recursively applying the
// following rules in the given order to x and y and all of their sub-values:
//
// - Let S be the set of all [Ignore], [Transformer], and [Comparer] options that
// remain after applying all path filters, value filters, and type filters.
// If at least one [Ignore] exists in S, then the comparison is ignored.
// If the number of [Transformer] and [Comparer] options in S is non-zero,
// then Equal panics because it is ambiguous which option to use.
// If S contains a single [Transformer], then use that to transform
// the current values and recursively call Equal on the output values.
// If S contains a single [Comparer], then use that to compare the current values.
// Otherwise, evaluation proceeds to the next rule.
//
// - If the values have an Equal method of the form "(T) Equal(T) bool" or
// "(T) Equal(I) bool" where T is assignable to I, then use the result of
// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and
// evaluation proceeds to the next rule.
//
// - Lastly, try to compare x and y based on their basic kinds.
// Simple kinds like booleans, integers, floats, complex numbers, strings,
// and channels are compared using the equivalent of the == operator in Go.
// Functions are only equal if they are both nil, otherwise they are unequal.
//
// Structs are equal if recursively calling Equal on all fields report equal.
// If a struct contains unexported fields, Equal panics unless an [Ignore] option
// (e.g., [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) ignores that field
// or the [Exporter] option explicitly permits comparing the unexported field.
//
// Slices are equal if they are both nil or both non-nil, where recursively
// calling Equal on all non-ignored slice or array elements report equal.
// Empty non-nil slices and nil slices are not equal; to equate empty slices,
// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty].
//
// Maps are equal if they are both nil or both non-nil, where recursively
// calling Equal on all non-ignored map entries report equal.
// Map keys are equal according to the == operator.
// To use custom comparisons for map keys, consider using
// [github.com/google/go-cmp/cmp/cmpopts.SortMaps].
// Empty non-nil maps and nil maps are not equal; to equate empty maps,
// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty].
//
// Pointers and interfaces are equal if they are both nil or both non-nil,
// where they have the same underlying concrete type and recursively
// calling Equal on the underlying values reports equal.
//
// Before recursing into a pointer, slice element, or map, the current path
// is checked to detect whether the address has already been visited.
// If there is a cycle, then the pointed at values are considered equal
// only if both addresses were previously visited in the same path step.
func Equal(x, y interface{}, opts ...Option) bool {
s := newState(opts)
s.compareAny(rootStep(x, y))
return s.result.Equal()
}
// Diff returns a human-readable report of the differences between two values:
// y - x. It returns an empty string if and only if Equal returns true for the
// same input values and options.
//
// The output is displayed as a literal in pseudo-Go syntax.
// At the start of each line, a "-" prefix indicates an element removed from x,
// a "+" prefix to indicates an element added from y, and the lack of a prefix
// indicates an element common to both x and y. If possible, the output
// uses fmt.Stringer.String or error.Error methods to produce more humanly
// readable outputs. In such cases, the string is prefixed with either an
// 's' or 'e' character, respectively, to indicate that the method was called.
//
// Do not depend on this output being stable. If you need the ability to
// programmatically interpret the difference, consider using a custom Reporter.
func Diff(x, y interface{}, opts ...Option) string {
s := newState(opts)
// Optimization: If there are no other reporters, we can optimize for the
// common case where the result is equal (and thus no reported difference).
// This avoids the expensive construction of a difference tree.
if len(s.reporters) == 0 {
s.compareAny(rootStep(x, y))
if s.result.Equal() {
return ""
}
s.result = diff.Result{} // Reset results
}
r := new(defaultReporter)
s.reporters = append(s.reporters, reporter{r})
s.compareAny(rootStep(x, y))
d := r.String()
if (d == "") != s.result.Equal() {
panic("inconsistent difference and equality results")
}
return d
}
// rootStep constructs the first path step. If x and y have differing types,
// then they are stored within an empty interface type.
func rootStep(x, y interface{}) PathStep {
vx := reflect.ValueOf(x)
vy := reflect.ValueOf(y)
// If the inputs are different types, auto-wrap them in an empty interface
// so that they have the same parent type.
var t reflect.Type
if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() {
t = anyType
if vx.IsValid() {
vvx := reflect.New(t).Elem()
vvx.Set(vx)
vx = vvx
}
if vy.IsValid() {
vvy := reflect.New(t).Elem()
vvy.Set(vy)
vy = vvy
}
} else {
t = vx.Type()
}
return &pathStep{t, vx, vy}
}
type state struct {
// These fields represent the "comparison state".
// Calling statelessCompare must not result in observable changes to these.
result diff.Result // The current result of comparison
curPath Path // The current path in the value tree
curPtrs pointerPath // The current set of visited pointers
reporters []reporter // Optional reporters
// recChecker checks for infinite cycles applying the same set of
// transformers upon the output of itself.
recChecker recChecker
// dynChecker triggers pseudo-random checks for option correctness.
// It is safe for statelessCompare to mutate this value.
dynChecker dynChecker
// These fields, once set by processOption, will not change.
exporters []exporter // List of exporters for structs with unexported fields
opts Options // List of all fundamental and filter options
}
func newState(opts []Option) *state {
// Always ensure a validator option exists to validate the inputs.
s := &state{opts: Options{validator{}}}
s.curPtrs.Init()
s.processOption(Options(opts))
return s
}
func (s *state) processOption(opt Option) {
switch opt := opt.(type) {
case nil:
case Options:
for _, o := range opt {
s.processOption(o)
}
case coreOption:
type filtered interface {
isFiltered() bool
}
if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() {
panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt))
}
s.opts = append(s.opts, opt)
case exporter:
s.exporters = append(s.exporters, opt)
case reporter:
s.reporters = append(s.reporters, opt)
default:
panic(fmt.Sprintf("unknown option %T", opt))
}
}
// statelessCompare compares two values and returns the result.
// This function is stateless in that it does not alter the current result,
// or output to any registered reporters.
func (s *state) statelessCompare(step PathStep) diff.Result {
// We do not save and restore curPath and curPtrs because all of the
// compareX methods should properly push and pop from them.
// It is an implementation bug if the contents of the paths differ from
// when calling this function to when returning from it.
oldResult, oldReporters := s.result, s.reporters
s.result = diff.Result{} // Reset result
s.reporters = nil // Remove reporters to avoid spurious printouts
s.compareAny(step)
res := s.result
s.result, s.reporters = oldResult, oldReporters
return res
}
func (s *state) compareAny(step PathStep) {
// Update the path stack.
s.curPath.push(step)
defer s.curPath.pop()
for _, r := range s.reporters {
r.PushStep(step)
defer r.PopStep()
}
s.recChecker.Check(s.curPath)
// Cycle-detection for slice elements (see NOTE in compareSlice).
t := step.Type()
vx, vy := step.Values()
if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() {
px, py := vx.Addr(), vy.Addr()
if eq, visited := s.curPtrs.Push(px, py); visited {
s.report(eq, reportByCycle)
return
}
defer s.curPtrs.Pop(px, py)
}
// Rule 1: Check whether an option applies on this node in the value tree.
if s.tryOptions(t, vx, vy) {
return
}
// Rule 2: Check whether the type has a valid Equal method.
if s.tryMethod(t, vx, vy) {
return
}
// Rule 3: Compare based on the underlying kind.
switch t.Kind() {
case reflect.Bool:
s.report(vx.Bool() == vy.Bool(), 0)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s.report(vx.Int() == vy.Int(), 0)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
s.report(vx.Uint() == vy.Uint(), 0)
case reflect.Float32, reflect.Float64:
s.report(vx.Float() == vy.Float(), 0)
case reflect.Complex64, reflect.Complex128:
s.report(vx.Complex() == vy.Complex(), 0)
case reflect.String:
s.report(vx.String() == vy.String(), 0)
case reflect.Chan, reflect.UnsafePointer:
s.report(vx.Pointer() == vy.Pointer(), 0)
case reflect.Func:
s.report(vx.IsNil() && vy.IsNil(), 0)
case reflect.Struct:
s.compareStruct(t, vx, vy)
case reflect.Slice, reflect.Array:
s.compareSlice(t, vx, vy)
case reflect.Map:
s.compareMap(t, vx, vy)
case reflect.Ptr:
s.comparePtr(t, vx, vy)
case reflect.Interface:
s.compareInterface(t, vx, vy)
default:
panic(fmt.Sprintf("%v kind not handled", t.Kind()))
}
}
func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool {
// Evaluate all filters and apply the remaining options.
if opt := s.opts.filter(s, t, vx, vy); opt != nil {
opt.apply(s, vx, vy)
return true
}
return false
}
func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool {
// Check if this type even has an Equal method.
m, ok := t.MethodByName("Equal")
if !ok || !function.IsType(m.Type, function.EqualAssignable) {
return false
}
eq := s.callTTBFunc(m.Func, vx, vy)
s.report(eq, reportByMethod)
return true
}
func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value {
if !s.dynChecker.Next() {
return f.Call([]reflect.Value{v})[0]
}
// Run the function twice and ensure that we get the same results back.
// We run in goroutines so that the race detector (if enabled) can detect
// unsafe mutations to the input.
c := make(chan reflect.Value)
go detectRaces(c, f, v)
got := <-c
want := f.Call([]reflect.Value{v})[0]
if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() {
// To avoid false-positives with non-reflexive equality operations,
// we sanity check whether a value is equal to itself.
if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() {
return want
}
panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f)))
}
return want
}
func (s *state) callTTBFunc(f, x, y reflect.Value) bool {
if !s.dynChecker.Next() {
return f.Call([]reflect.Value{x, y})[0].Bool()
}
// Swapping the input arguments is sufficient to check that
// f is symmetric and deterministic.
// We run in goroutines so that the race detector (if enabled) can detect
// unsafe mutations to the input.
c := make(chan reflect.Value)
go detectRaces(c, f, y, x)
got := <-c
want := f.Call([]reflect.Value{x, y})[0].Bool()
if !got.IsValid() || got.Bool() != want {
panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f)))
}
return want
}
func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) {
var ret reflect.Value
defer func() {
recover() // Ignore panics, let the other call to f panic instead
c <- ret
}()
ret = f.Call(vs)[0]
}
func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
var addr bool
var vax, vay reflect.Value // Addressable versions of vx and vy
var mayForce, mayForceInit bool
step := StructField{&structField{}}
for i := 0; i < t.NumField(); i++ {
step.typ = t.Field(i).Type
step.vx = vx.Field(i)
step.vy = vy.Field(i)
step.name = t.Field(i).Name
step.idx = i
step.unexported = !isExported(step.name)
if step.unexported {
if step.name == "_" {
continue
}
// Defer checking of unexported fields until later to give an
// Ignore a chance to ignore the field.
if !vax.IsValid() || !vay.IsValid() {
// For retrieveUnexportedField to work, the parent struct must
// be addressable. Create a new copy of the values if
// necessary to make them addressable.
addr = vx.CanAddr() || vy.CanAddr()
vax = makeAddressable(vx)
vay = makeAddressable(vy)
}
if !mayForceInit {
for _, xf := range s.exporters {
mayForce = mayForce || xf(t)
}
mayForceInit = true
}
step.mayForce = mayForce
step.paddr = addr
step.pvx = vax
step.pvy = vay
step.field = t.Field(i)
}
s.compareAny(step)
}
}
func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) {
isSlice := t.Kind() == reflect.Slice
if isSlice && (vx.IsNil() || vy.IsNil()) {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
// NOTE: It is incorrect to call curPtrs.Push on the slice header pointer
// since slices represents a list of pointers, rather than a single pointer.
// The pointer checking logic must be handled on a per-element basis
// in compareAny.
//
// A slice header (see reflect.SliceHeader) in Go is a tuple of a starting
// pointer P, a length N, and a capacity C. Supposing each slice element has
// a memory size of M, then the slice is equivalent to the list of pointers:
// [P+i*M for i in range(N)]
//
// For example, v[:0] and v[:1] are slices with the same starting pointer,
// but they are clearly different values. Using the slice pointer alone
// violates the assumption that equal pointers implies equal values.
step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}}
withIndexes := func(ix, iy int) SliceIndex {
if ix >= 0 {
step.vx, step.xkey = vx.Index(ix), ix
} else {
step.vx, step.xkey = reflect.Value{}, -1
}
if iy >= 0 {
step.vy, step.ykey = vy.Index(iy), iy
} else {
step.vy, step.ykey = reflect.Value{}, -1
}
return step
}
// Ignore options are able to ignore missing elements in a slice.
// However, detecting these reliably requires an optimal differencing
// algorithm, for which diff.Difference is not.
//
// Instead, we first iterate through both slices to detect which elements
// would be ignored if standing alone. The index of non-discarded elements
// are stored in a separate slice, which diffing is then performed on.
var indexesX, indexesY []int
var ignoredX, ignoredY []bool
for ix := 0; ix < vx.Len(); ix++ {
ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0
if !ignored {
indexesX = append(indexesX, ix)
}
ignoredX = append(ignoredX, ignored)
}
for iy := 0; iy < vy.Len(); iy++ {
ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0
if !ignored {
indexesY = append(indexesY, iy)
}
ignoredY = append(ignoredY, ignored)
}
// Compute an edit-script for slices vx and vy (excluding ignored elements).
edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result {
return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy]))
})
// Replay the ignore-scripts and the edit-script.
var ix, iy int
for ix < vx.Len() || iy < vy.Len() {
var e diff.EditType
switch {
case ix < len(ignoredX) && ignoredX[ix]:
e = diff.UniqueX
case iy < len(ignoredY) && ignoredY[iy]:
e = diff.UniqueY
default:
e, edits = edits[0], edits[1:]
}
switch e {
case diff.UniqueX:
s.compareAny(withIndexes(ix, -1))
ix++
case diff.UniqueY:
s.compareAny(withIndexes(-1, iy))
iy++
default:
s.compareAny(withIndexes(ix, iy))
ix++
iy++
}
}
}
func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) {
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
// Cycle-detection for maps.
if eq, visited := s.curPtrs.Push(vx, vy); visited {
s.report(eq, reportByCycle)
return
}
defer s.curPtrs.Pop(vx, vy)
// We combine and sort the two map keys so that we can perform the
// comparisons in a deterministic order.
step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}}
for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) {
step.vx = vx.MapIndex(k)
step.vy = vy.MapIndex(k)
step.key = k
if !step.vx.IsValid() && !step.vy.IsValid() {
// It is possible for both vx and vy to be invalid if the
// key contained a NaN value in it.
//
// Even with the ability to retrieve NaN keys in Go 1.12,
// there still isn't a sensible way to compare the values since
// a NaN key may map to multiple unordered values.
// The most reasonable way to compare NaNs would be to compare the
// set of values. However, this is impossible to do efficiently
// since set equality is provably an O(n^2) operation given only
// an Equal function. If we had a Less function or Hash function,
// this could be done in O(n*log(n)) or O(n), respectively.
//
// Rather than adding complex logic to deal with NaNs, make it
// the user's responsibility to compare such obscure maps.
const help = "consider providing a Comparer to compare the map"
panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help))
}
s.compareAny(step)
}
}
func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) {
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
// Cycle-detection for pointers.
if eq, visited := s.curPtrs.Push(vx, vy); visited {
s.report(eq, reportByCycle)
return
}
defer s.curPtrs.Pop(vx, vy)
vx, vy = vx.Elem(), vy.Elem()
s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}})
}
func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) {
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
vx, vy = vx.Elem(), vy.Elem()
if vx.Type() != vy.Type() {
s.report(false, 0)
return
}
s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}})
}
func (s *state) report(eq bool, rf resultFlags) {
if rf&reportByIgnore == 0 {
if eq {
s.result.NumSame++
rf |= reportEqual
} else {
s.result.NumDiff++
rf |= reportUnequal
}
}
for _, r := range s.reporters {
r.Report(Result{flags: rf})
}
}
// recChecker tracks the state needed to periodically perform checks that
// user provided transformers are not stuck in an infinitely recursive cycle.
type recChecker struct{ next int }
// Check scans the Path for any recursive transformers and panics when any
// recursive transformers are detected. Note that the presence of a
// recursive Transformer does not necessarily imply an infinite cycle.
// As such, this check only activates after some minimal number of path steps.
func (rc *recChecker) Check(p Path) {
const minLen = 1 << 16
if rc.next == 0 {
rc.next = minLen
}
if len(p) < rc.next {
return
}
rc.next <<= 1
// Check whether the same transformer has appeared at least twice.
var ss []string
m := map[Option]int{}
for _, ps := range p {
if t, ok := ps.(Transform); ok {
t := t.Option()
if m[t] == 1 { // Transformer was used exactly once before
tf := t.(*transformer).fnc.Type()
ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0)))
}
m[t]++
}
}
if len(ss) > 0 {
const warning = "recursive set of Transformers detected"
const help = "consider using cmpopts.AcyclicTransformer"
set := strings.Join(ss, "\n\t")
panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help))
}
}
// dynChecker tracks the state needed to periodically perform checks that
// user provided functions are symmetric and deterministic.
// The zero value is safe for immediate use.
type dynChecker struct{ curr, next int }
// Next increments the state and reports whether a check should be performed.
//
// Checks occur every Nth function call, where N is a triangular number:
//
// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ...
//
// See https://en.wikipedia.org/wiki/Triangular_number
//
// This sequence ensures that the cost of checks drops significantly as
// the number of functions calls grows larger.
func (dc *dynChecker) Next() bool {
ok := dc.curr == dc.next
if ok {
dc.curr = 0
dc.next++
}
dc.curr++
return ok
}
// makeAddressable returns a value that is always addressable.
// It returns the input verbatim if it is already addressable,
// otherwise it creates a new value and returns an addressable copy.
func makeAddressable(v reflect.Value) reflect.Value {
if v.CanAddr() {
return v
}
vc := reflect.New(v.Type()).Elem()
vc.Set(v)
return vc
}

31
vendor/github.com/google/go-cmp/cmp/export.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"reflect"
"unsafe"
)
// retrieveUnexportedField uses unsafe to forcibly retrieve any field from
// a struct such that the value has read-write permissions.
//
// The parent struct, v, must be addressable, while f must be a StructField
// describing the field to retrieve. If addr is false,
// then the returned value will be shallowed copied to be non-addressable.
func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value {
ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem()
if !addr {
// A field is addressable if and only if the struct is addressable.
// If the original parent value was not addressable, shallow copy the
// value to make it non-addressable to avoid leaking an implementation
// detail of how forcibly exporting a field works.
if ve.Kind() == reflect.Interface && ve.IsNil() {
return reflect.Zero(f.Type)
}
return reflect.ValueOf(ve.Interface()).Convert(f.Type)
}
return ve
}

View File

@@ -0,0 +1,18 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !cmp_debug
// +build !cmp_debug
package diff
var debug debugger
type debugger struct{}
func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc {
return f
}
func (debugger) Update() {}
func (debugger) Finish() {}

View File

@@ -0,0 +1,123 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cmp_debug
// +build cmp_debug
package diff
import (
"fmt"
"strings"
"sync"
"time"
)
// The algorithm can be seen running in real-time by enabling debugging:
// go test -tags=cmp_debug -v
//
// Example output:
// === RUN TestDifference/#34
// ┌───────────────────────────────┐
// │ \ · · · · · · · · · · · · · · │
// │ · # · · · · · · · · · · · · · │
// │ · \ · · · · · · · · · · · · · │
// │ · · \ · · · · · · · · · · · · │
// │ · · · X # · · · · · · · · · · │
// │ · · · # \ · · · · · · · · · · │
// │ · · · · · # # · · · · · · · · │
// │ · · · · · # \ · · · · · · · · │
// │ · · · · · · · \ · · · · · · · │
// │ · · · · · · · · \ · · · · · · │
// │ · · · · · · · · · \ · · · · · │
// │ · · · · · · · · · · \ · · # · │
// │ · · · · · · · · · · · \ # # · │
// │ · · · · · · · · · · · # # # · │
// │ · · · · · · · · · · # # # # · │
// │ · · · · · · · · · # # # # # · │
// │ · · · · · · · · · · · · · · \ │
// └───────────────────────────────┘
// [.Y..M.XY......YXYXY.|]
//
// The grid represents the edit-graph where the horizontal axis represents
// list X and the vertical axis represents list Y. The start of the two lists
// is the top-left, while the ends are the bottom-right. The '·' represents
// an unexplored node in the graph. The '\' indicates that the two symbols
// from list X and Y are equal. The 'X' indicates that two symbols are similar
// (but not exactly equal) to each other. The '#' indicates that the two symbols
// are different (and not similar). The algorithm traverses this graph trying to
// make the paths starting in the top-left and the bottom-right connect.
//
// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents
// the currently established path from the forward and reverse searches,
// separated by a '|' character.
const (
updateDelay = 100 * time.Millisecond
finishDelay = 500 * time.Millisecond
ansiTerminal = true // ANSI escape codes used to move terminal cursor
)
var debug debugger
type debugger struct {
sync.Mutex
p1, p2 EditScript
fwdPath, revPath *EditScript
grid []byte
lines int
}
func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc {
dbg.Lock()
dbg.fwdPath, dbg.revPath = p1, p2
top := "┌─" + strings.Repeat("──", nx) + "┐\n"
row := "│ " + strings.Repeat("· ", nx) + "│\n"
btm := "└─" + strings.Repeat("──", nx) + "┘\n"
dbg.grid = []byte(top + strings.Repeat(row, ny) + btm)
dbg.lines = strings.Count(dbg.String(), "\n")
fmt.Print(dbg)
// Wrap the EqualFunc so that we can intercept each result.
return func(ix, iy int) (r Result) {
cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")]
for i := range cell {
cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot
}
switch r = f(ix, iy); {
case r.Equal():
cell[0] = '\\'
case r.Similar():
cell[0] = 'X'
default:
cell[0] = '#'
}
return
}
}
func (dbg *debugger) Update() {
dbg.print(updateDelay)
}
func (dbg *debugger) Finish() {
dbg.print(finishDelay)
dbg.Unlock()
}
func (dbg *debugger) String() string {
dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0]
for i := len(*dbg.revPath) - 1; i >= 0; i-- {
dbg.p2 = append(dbg.p2, (*dbg.revPath)[i])
}
return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2)
}
func (dbg *debugger) print(d time.Duration) {
if ansiTerminal {
fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor
}
fmt.Print(dbg)
time.Sleep(d)
}

View File

@@ -0,0 +1,402 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package diff implements an algorithm for producing edit-scripts.
// The edit-script is a sequence of operations needed to transform one list
// of symbols into another (or vice-versa). The edits allowed are insertions,
// deletions, and modifications. The summation of all edits is called the
// Levenshtein distance as this problem is well-known in computer science.
//
// This package prioritizes performance over accuracy. That is, the run time
// is more important than obtaining a minimal Levenshtein distance.
package diff
import (
"math/rand"
"time"
"github.com/google/go-cmp/cmp/internal/flags"
)
// EditType represents a single operation within an edit-script.
type EditType uint8
const (
// Identity indicates that a symbol pair is identical in both list X and Y.
Identity EditType = iota
// UniqueX indicates that a symbol only exists in X and not Y.
UniqueX
// UniqueY indicates that a symbol only exists in Y and not X.
UniqueY
// Modified indicates that a symbol pair is a modification of each other.
Modified
)
// EditScript represents the series of differences between two lists.
type EditScript []EditType
// String returns a human-readable string representing the edit-script where
// Identity, UniqueX, UniqueY, and Modified are represented by the
// '.', 'X', 'Y', and 'M' characters, respectively.
func (es EditScript) String() string {
b := make([]byte, len(es))
for i, e := range es {
switch e {
case Identity:
b[i] = '.'
case UniqueX:
b[i] = 'X'
case UniqueY:
b[i] = 'Y'
case Modified:
b[i] = 'M'
default:
panic("invalid edit-type")
}
}
return string(b)
}
// stats returns a histogram of the number of each type of edit operation.
func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) {
for _, e := range es {
switch e {
case Identity:
s.NI++
case UniqueX:
s.NX++
case UniqueY:
s.NY++
case Modified:
s.NM++
default:
panic("invalid edit-type")
}
}
return
}
// Dist is the Levenshtein distance and is guaranteed to be 0 if and only if
// lists X and Y are equal.
func (es EditScript) Dist() int { return len(es) - es.stats().NI }
// LenX is the length of the X list.
func (es EditScript) LenX() int { return len(es) - es.stats().NY }
// LenY is the length of the Y list.
func (es EditScript) LenY() int { return len(es) - es.stats().NX }
// EqualFunc reports whether the symbols at indexes ix and iy are equal.
// When called by Difference, the index is guaranteed to be within nx and ny.
type EqualFunc func(ix int, iy int) Result
// Result is the result of comparison.
// NumSame is the number of sub-elements that are equal.
// NumDiff is the number of sub-elements that are not equal.
type Result struct{ NumSame, NumDiff int }
// BoolResult returns a Result that is either Equal or not Equal.
func BoolResult(b bool) Result {
if b {
return Result{NumSame: 1} // Equal, Similar
} else {
return Result{NumDiff: 2} // Not Equal, not Similar
}
}
// Equal indicates whether the symbols are equal. Two symbols are equal
// if and only if NumDiff == 0. If Equal, then they are also Similar.
func (r Result) Equal() bool { return r.NumDiff == 0 }
// Similar indicates whether two symbols are similar and may be represented
// by using the Modified type. As a special case, we consider binary comparisons
// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar.
//
// The exact ratio of NumSame to NumDiff to determine similarity may change.
func (r Result) Similar() bool {
// Use NumSame+1 to offset NumSame so that binary comparisons are similar.
return r.NumSame+1 >= r.NumDiff
}
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
// Difference reports whether two lists of lengths nx and ny are equal
// given the definition of equality provided as f.
//
// This function returns an edit-script, which is a sequence of operations
// needed to convert one list into the other. The following invariants for
// the edit-script are maintained:
// - eq == (es.Dist()==0)
// - nx == es.LenX()
// - ny == es.LenY()
//
// This algorithm is not guaranteed to be an optimal solution (i.e., one that
// produces an edit-script with a minimal Levenshtein distance). This algorithm
// favors performance over optimality. The exact output is not guaranteed to
// be stable and may change over time.
func Difference(nx, ny int, f EqualFunc) (es EditScript) {
// This algorithm is based on traversing what is known as an "edit-graph".
// See Figure 1 from "An O(ND) Difference Algorithm and Its Variations"
// by Eugene W. Myers. Since D can be as large as N itself, this is
// effectively O(N^2). Unlike the algorithm from that paper, we are not
// interested in the optimal path, but at least some "decent" path.
//
// For example, let X and Y be lists of symbols:
// X = [A B C A B B A]
// Y = [C B A B A C]
//
// The edit-graph can be drawn as the following:
// A B C A B B A
// ┌─────────────┐
// C │_|_|\|_|_|_|_│ 0
// B │_|\|_|_|\|\|_│ 1
// A │\|_|_|\|_|_|\│ 2
// B │_|\|_|_|\|\|_│ 3
// A │\|_|_|\|_|_|\│ 4
// C │ | |\| | | | │ 5
// └─────────────┘ 6
// 0 1 2 3 4 5 6 7
//
// List X is written along the horizontal axis, while list Y is written
// along the vertical axis. At any point on this grid, if the symbol in
// list X matches the corresponding symbol in list Y, then a '\' is drawn.
// The goal of any minimal edit-script algorithm is to find a path from the
// top-left corner to the bottom-right corner, while traveling through the
// fewest horizontal or vertical edges.
// A horizontal edge is equivalent to inserting a symbol from list X.
// A vertical edge is equivalent to inserting a symbol from list Y.
// A diagonal edge is equivalent to a matching symbol between both X and Y.
// Invariants:
// - 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx
// - 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny
//
// In general:
// - fwdFrontier.X < revFrontier.X
// - fwdFrontier.Y < revFrontier.Y
//
// Unless, it is time for the algorithm to terminate.
fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)}
revPath := path{-1, point{nx, ny}, make(EditScript, 0)}
fwdFrontier := fwdPath.point // Forward search frontier
revFrontier := revPath.point // Reverse search frontier
// Search budget bounds the cost of searching for better paths.
// The longest sequence of non-matching symbols that can be tolerated is
// approximately the square-root of the search budget.
searchBudget := 4 * (nx + ny) // O(n)
// Running the tests with the "cmp_debug" build tag prints a visualization
// of the algorithm running in real-time. This is educational for
// understanding how the algorithm works. See debug_enable.go.
f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es)
// The algorithm below is a greedy, meet-in-the-middle algorithm for
// computing sub-optimal edit-scripts between two lists.
//
// The algorithm is approximately as follows:
// - Searching for differences switches back-and-forth between
// a search that starts at the beginning (the top-left corner), and
// a search that starts at the end (the bottom-right corner).
// The goal of the search is connect with the search
// from the opposite corner.
// - As we search, we build a path in a greedy manner,
// where the first match seen is added to the path (this is sub-optimal,
// but provides a decent result in practice). When matches are found,
// we try the next pair of symbols in the lists and follow all matches
// as far as possible.
// - When searching for matches, we search along a diagonal going through
// through the "frontier" point. If no matches are found,
// we advance the frontier towards the opposite corner.
// - This algorithm terminates when either the X coordinates or the
// Y coordinates of the forward and reverse frontier points ever intersect.
// This algorithm is correct even if searching only in the forward direction
// or in the reverse direction. We do both because it is commonly observed
// that two lists commonly differ because elements were added to the front
// or end of the other list.
//
// Non-deterministically start with either the forward or reverse direction
// to introduce some deliberate instability so that we have the flexibility
// to change this algorithm in the future.
if flags.Deterministic || randBool {
goto forwardSearch
} else {
goto reverseSearch
}
forwardSearch:
{
// Forward search from the beginning.
if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
goto finishSearch
}
for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ {
// Search in a diagonal pattern for a match.
z := zigzag(i)
p := point{fwdFrontier.X + z, fwdFrontier.Y - z}
switch {
case p.X >= revPath.X || p.Y < fwdPath.Y:
stop1 = true // Hit top-right corner
case p.Y >= revPath.Y || p.X < fwdPath.X:
stop2 = true // Hit bottom-left corner
case f(p.X, p.Y).Equal():
// Match found, so connect the path to this point.
fwdPath.connect(p, f)
fwdPath.append(Identity)
// Follow sequence of matches as far as possible.
for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y {
if !f(fwdPath.X, fwdPath.Y).Equal() {
break
}
fwdPath.append(Identity)
}
fwdFrontier = fwdPath.point
stop1, stop2 = true, true
default:
searchBudget-- // Match not found
}
debug.Update()
}
// Advance the frontier towards reverse point.
if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y {
fwdFrontier.X++
} else {
fwdFrontier.Y++
}
goto reverseSearch
}
reverseSearch:
{
// Reverse search from the end.
if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
goto finishSearch
}
for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ {
// Search in a diagonal pattern for a match.
z := zigzag(i)
p := point{revFrontier.X - z, revFrontier.Y + z}
switch {
case fwdPath.X >= p.X || revPath.Y < p.Y:
stop1 = true // Hit bottom-left corner
case fwdPath.Y >= p.Y || revPath.X < p.X:
stop2 = true // Hit top-right corner
case f(p.X-1, p.Y-1).Equal():
// Match found, so connect the path to this point.
revPath.connect(p, f)
revPath.append(Identity)
// Follow sequence of matches as far as possible.
for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y {
if !f(revPath.X-1, revPath.Y-1).Equal() {
break
}
revPath.append(Identity)
}
revFrontier = revPath.point
stop1, stop2 = true, true
default:
searchBudget-- // Match not found
}
debug.Update()
}
// Advance the frontier towards forward point.
if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y {
revFrontier.X--
} else {
revFrontier.Y--
}
goto forwardSearch
}
finishSearch:
// Join the forward and reverse paths and then append the reverse path.
fwdPath.connect(revPath.point, f)
for i := len(revPath.es) - 1; i >= 0; i-- {
t := revPath.es[i]
revPath.es = revPath.es[:i]
fwdPath.append(t)
}
debug.Finish()
return fwdPath.es
}
type path struct {
dir int // +1 if forward, -1 if reverse
point // Leading point of the EditScript path
es EditScript
}
// connect appends any necessary Identity, Modified, UniqueX, or UniqueY types
// to the edit-script to connect p.point to dst.
func (p *path) connect(dst point, f EqualFunc) {
if p.dir > 0 {
// Connect in forward direction.
for dst.X > p.X && dst.Y > p.Y {
switch r := f(p.X, p.Y); {
case r.Equal():
p.append(Identity)
case r.Similar():
p.append(Modified)
case dst.X-p.X >= dst.Y-p.Y:
p.append(UniqueX)
default:
p.append(UniqueY)
}
}
for dst.X > p.X {
p.append(UniqueX)
}
for dst.Y > p.Y {
p.append(UniqueY)
}
} else {
// Connect in reverse direction.
for p.X > dst.X && p.Y > dst.Y {
switch r := f(p.X-1, p.Y-1); {
case r.Equal():
p.append(Identity)
case r.Similar():
p.append(Modified)
case p.Y-dst.Y >= p.X-dst.X:
p.append(UniqueY)
default:
p.append(UniqueX)
}
}
for p.X > dst.X {
p.append(UniqueX)
}
for p.Y > dst.Y {
p.append(UniqueY)
}
}
}
func (p *path) append(t EditType) {
p.es = append(p.es, t)
switch t {
case Identity, Modified:
p.add(p.dir, p.dir)
case UniqueX:
p.add(p.dir, 0)
case UniqueY:
p.add(0, p.dir)
}
debug.Update()
}
type point struct{ X, Y int }
func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy }
// zigzag maps a consecutive sequence of integers to a zig-zag sequence.
//
// [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...]
func zigzag(x int) int {
if x&1 != 0 {
x = ^x
}
return x >> 1
}

View File

@@ -0,0 +1,9 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flags
// Deterministic controls whether the output of Diff should be deterministic.
// This is only used for testing.
var Deterministic bool

View File

@@ -0,0 +1,99 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package function provides functionality for identifying function types.
package function
import (
"reflect"
"regexp"
"runtime"
"strings"
)
type funcType int
const (
_ funcType = iota
tbFunc // func(T) bool
ttbFunc // func(T, T) bool
trbFunc // func(T, R) bool
tibFunc // func(T, I) bool
trFunc // func(T) R
Equal = ttbFunc // func(T, T) bool
EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
Transformer = trFunc // func(T) R
ValueFilter = ttbFunc // func(T, T) bool
Less = ttbFunc // func(T, T) bool
ValuePredicate = tbFunc // func(T) bool
KeyValuePredicate = trbFunc // func(T, R) bool
)
var boolType = reflect.TypeOf(true)
// IsType reports whether the reflect.Type is of the specified function type.
func IsType(t reflect.Type, ft funcType) bool {
if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
return false
}
ni, no := t.NumIn(), t.NumOut()
switch ft {
case tbFunc: // func(T) bool
if ni == 1 && no == 1 && t.Out(0) == boolType {
return true
}
case ttbFunc: // func(T, T) bool
if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType {
return true
}
case trbFunc: // func(T, R) bool
if ni == 2 && no == 1 && t.Out(0) == boolType {
return true
}
case tibFunc: // func(T, I) bool
if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType {
return true
}
case trFunc: // func(T) R
if ni == 1 && no == 1 {
return true
}
}
return false
}
var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`)
// NameOf returns the name of the function value.
func NameOf(v reflect.Value) string {
fnc := runtime.FuncForPC(v.Pointer())
if fnc == nil {
return "<unknown>"
}
fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm"
// Method closures have a "-fm" suffix.
fullName = strings.TrimSuffix(fullName, "-fm")
var name string
for len(fullName) > 0 {
inParen := strings.HasSuffix(fullName, ")")
fullName = strings.TrimSuffix(fullName, ")")
s := lastIdentRx.FindString(fullName)
if s == "" {
break
}
name = s + "." + name
fullName = strings.TrimSuffix(fullName, s)
if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 {
fullName = fullName[:i]
}
fullName = strings.TrimSuffix(fullName, ".")
}
return strings.TrimSuffix(name, ".")
}

View File

@@ -0,0 +1,164 @@
// Copyright 2020, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package value
import (
"reflect"
"strconv"
)
var anyType = reflect.TypeOf((*interface{})(nil)).Elem()
// TypeString is nearly identical to reflect.Type.String,
// but has an additional option to specify that full type names be used.
func TypeString(t reflect.Type, qualified bool) string {
return string(appendTypeName(nil, t, qualified, false))
}
func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte {
// BUG: Go reflection provides no way to disambiguate two named types
// of the same name and within the same package,
// but declared within the namespace of different functions.
// Use the "any" alias instead of "interface{}" for better readability.
if t == anyType {
return append(b, "any"...)
}
// Named type.
if t.Name() != "" {
if qualified && t.PkgPath() != "" {
b = append(b, '"')
b = append(b, t.PkgPath()...)
b = append(b, '"')
b = append(b, '.')
b = append(b, t.Name()...)
} else {
b = append(b, t.String()...)
}
return b
}
// Unnamed type.
switch k := t.Kind(); k {
case reflect.Bool, reflect.String, reflect.UnsafePointer,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
b = append(b, k.String()...)
case reflect.Chan:
if t.ChanDir() == reflect.RecvDir {
b = append(b, "<-"...)
}
b = append(b, "chan"...)
if t.ChanDir() == reflect.SendDir {
b = append(b, "<-"...)
}
b = append(b, ' ')
b = appendTypeName(b, t.Elem(), qualified, false)
case reflect.Func:
if !elideFunc {
b = append(b, "func"...)
}
b = append(b, '(')
for i := 0; i < t.NumIn(); i++ {
if i > 0 {
b = append(b, ", "...)
}
if i == t.NumIn()-1 && t.IsVariadic() {
b = append(b, "..."...)
b = appendTypeName(b, t.In(i).Elem(), qualified, false)
} else {
b = appendTypeName(b, t.In(i), qualified, false)
}
}
b = append(b, ')')
switch t.NumOut() {
case 0:
// Do nothing
case 1:
b = append(b, ' ')
b = appendTypeName(b, t.Out(0), qualified, false)
default:
b = append(b, " ("...)
for i := 0; i < t.NumOut(); i++ {
if i > 0 {
b = append(b, ", "...)
}
b = appendTypeName(b, t.Out(i), qualified, false)
}
b = append(b, ')')
}
case reflect.Struct:
b = append(b, "struct{ "...)
for i := 0; i < t.NumField(); i++ {
if i > 0 {
b = append(b, "; "...)
}
sf := t.Field(i)
if !sf.Anonymous {
if qualified && sf.PkgPath != "" {
b = append(b, '"')
b = append(b, sf.PkgPath...)
b = append(b, '"')
b = append(b, '.')
}
b = append(b, sf.Name...)
b = append(b, ' ')
}
b = appendTypeName(b, sf.Type, qualified, false)
if sf.Tag != "" {
b = append(b, ' ')
b = strconv.AppendQuote(b, string(sf.Tag))
}
}
if b[len(b)-1] == ' ' {
b = b[:len(b)-1]
} else {
b = append(b, ' ')
}
b = append(b, '}')
case reflect.Slice, reflect.Array:
b = append(b, '[')
if k == reflect.Array {
b = strconv.AppendUint(b, uint64(t.Len()), 10)
}
b = append(b, ']')
b = appendTypeName(b, t.Elem(), qualified, false)
case reflect.Map:
b = append(b, "map["...)
b = appendTypeName(b, t.Key(), qualified, false)
b = append(b, ']')
b = appendTypeName(b, t.Elem(), qualified, false)
case reflect.Ptr:
b = append(b, '*')
b = appendTypeName(b, t.Elem(), qualified, false)
case reflect.Interface:
b = append(b, "interface{ "...)
for i := 0; i < t.NumMethod(); i++ {
if i > 0 {
b = append(b, "; "...)
}
m := t.Method(i)
if qualified && m.PkgPath != "" {
b = append(b, '"')
b = append(b, m.PkgPath...)
b = append(b, '"')
b = append(b, '.')
}
b = append(b, m.Name...)
b = appendTypeName(b, m.Type, qualified, true)
}
if b[len(b)-1] == ' ' {
b = b[:len(b)-1]
} else {
b = append(b, ' ')
}
b = append(b, '}')
default:
panic("invalid kind: " + k.String())
}
return b
}

View File

@@ -0,0 +1,34 @@
// Copyright 2018, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package value
import (
"reflect"
"unsafe"
)
// Pointer is an opaque typed pointer and is guaranteed to be comparable.
type Pointer struct {
p unsafe.Pointer
t reflect.Type
}
// PointerOf returns a Pointer from v, which must be a
// reflect.Ptr, reflect.Slice, or reflect.Map.
func PointerOf(v reflect.Value) Pointer {
// The proper representation of a pointer is unsafe.Pointer,
// which is necessary if the GC ever uses a moving collector.
return Pointer{unsafe.Pointer(v.Pointer()), v.Type()}
}
// IsNil reports whether the pointer is nil.
func (p Pointer) IsNil() bool {
return p.p == nil
}
// Uintptr returns the pointer as a uintptr.
func (p Pointer) Uintptr() uintptr {
return uintptr(p.p)
}

View File

@@ -0,0 +1,106 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package value
import (
"fmt"
"math"
"reflect"
"sort"
)
// SortKeys sorts a list of map keys, deduplicating keys if necessary.
// The type of each value must be comparable.
func SortKeys(vs []reflect.Value) []reflect.Value {
if len(vs) == 0 {
return vs
}
// Sort the map keys.
sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) })
// Deduplicate keys (fails for NaNs).
vs2 := vs[:1]
for _, v := range vs[1:] {
if isLess(vs2[len(vs2)-1], v) {
vs2 = append(vs2, v)
}
}
return vs2
}
// isLess is a generic function for sorting arbitrary map keys.
// The inputs must be of the same type and must be comparable.
func isLess(x, y reflect.Value) bool {
switch x.Type().Kind() {
case reflect.Bool:
return !x.Bool() && y.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return x.Int() < y.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return x.Uint() < y.Uint()
case reflect.Float32, reflect.Float64:
// NOTE: This does not sort -0 as less than +0
// since Go maps treat -0 and +0 as equal keys.
fx, fy := x.Float(), y.Float()
return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy)
case reflect.Complex64, reflect.Complex128:
cx, cy := x.Complex(), y.Complex()
rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy)
if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) {
return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy)
}
return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry)
case reflect.Ptr, reflect.UnsafePointer, reflect.Chan:
return x.Pointer() < y.Pointer()
case reflect.String:
return x.String() < y.String()
case reflect.Array:
for i := 0; i < x.Len(); i++ {
if isLess(x.Index(i), y.Index(i)) {
return true
}
if isLess(y.Index(i), x.Index(i)) {
return false
}
}
return false
case reflect.Struct:
for i := 0; i < x.NumField(); i++ {
if isLess(x.Field(i), y.Field(i)) {
return true
}
if isLess(y.Field(i), x.Field(i)) {
return false
}
}
return false
case reflect.Interface:
vx, vy := x.Elem(), y.Elem()
if !vx.IsValid() || !vy.IsValid() {
return !vx.IsValid() && vy.IsValid()
}
tx, ty := vx.Type(), vy.Type()
if tx == ty {
return isLess(x.Elem(), y.Elem())
}
if tx.Kind() != ty.Kind() {
return vx.Kind() < vy.Kind()
}
if tx.String() != ty.String() {
return tx.String() < ty.String()
}
if tx.PkgPath() != ty.PkgPath() {
return tx.PkgPath() < ty.PkgPath()
}
// This can happen in rare situations, so we fallback to just comparing
// the unique pointer for a reflect.Type. This guarantees deterministic
// ordering within a program, but it is obviously not stable.
return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer()
default:
// Must be Func, Map, or Slice; which are not comparable.
panic(fmt.Sprintf("%T is not comparable", x.Type()))
}
}

554
vendor/github.com/google/go-cmp/cmp/options.go generated vendored Normal file
View File

@@ -0,0 +1,554 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"fmt"
"reflect"
"regexp"
"strings"
"github.com/google/go-cmp/cmp/internal/function"
)
// Option configures for specific behavior of [Equal] and [Diff]. In particular,
// the fundamental Option functions ([Ignore], [Transformer], and [Comparer]),
// configure how equality is determined.
//
// The fundamental options may be composed with filters ([FilterPath] and
// [FilterValues]) to control the scope over which they are applied.
//
// The [github.com/google/go-cmp/cmp/cmpopts] package provides helper functions
// for creating options that may be used with [Equal] and [Diff].
type Option interface {
// filter applies all filters and returns the option that remains.
// Each option may only read s.curPath and call s.callTTBFunc.
//
// An Options is returned only if multiple comparers or transformers
// can apply simultaneously and will only contain values of those types
// or sub-Options containing values of those types.
filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption
}
// applicableOption represents the following types:
//
// Fundamental: ignore | validator | *comparer | *transformer
// Grouping: Options
type applicableOption interface {
Option
// apply executes the option, which may mutate s or panic.
apply(s *state, vx, vy reflect.Value)
}
// coreOption represents the following types:
//
// Fundamental: ignore | validator | *comparer | *transformer
// Filters: *pathFilter | *valuesFilter
type coreOption interface {
Option
isCore()
}
type core struct{}
func (core) isCore() {}
// Options is a list of [Option] values that also satisfies the [Option] interface.
// Helper comparison packages may return an Options value when packing multiple
// [Option] values into a single [Option]. When this package processes an Options,
// it will be implicitly expanded into a flat list.
//
// Applying a filter on an Options is equivalent to applying that same filter
// on all individual options held within.
type Options []Option
func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) {
for _, opt := range opts {
switch opt := opt.filter(s, t, vx, vy); opt.(type) {
case ignore:
return ignore{} // Only ignore can short-circuit evaluation
case validator:
out = validator{} // Takes precedence over comparer or transformer
case *comparer, *transformer, Options:
switch out.(type) {
case nil:
out = opt
case validator:
// Keep validator
case *comparer, *transformer, Options:
out = Options{out, opt} // Conflicting comparers or transformers
}
}
}
return out
}
func (opts Options) apply(s *state, _, _ reflect.Value) {
const warning = "ambiguous set of applicable options"
const help = "consider using filters to ensure at most one Comparer or Transformer may apply"
var ss []string
for _, opt := range flattenOptions(nil, opts) {
ss = append(ss, fmt.Sprint(opt))
}
set := strings.Join(ss, "\n\t")
panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help))
}
func (opts Options) String() string {
var ss []string
for _, opt := range opts {
ss = append(ss, fmt.Sprint(opt))
}
return fmt.Sprintf("Options{%s}", strings.Join(ss, ", "))
}
// FilterPath returns a new [Option] where opt is only evaluated if filter f
// returns true for the current [Path] in the value tree.
//
// This filter is called even if a slice element or map entry is missing and
// provides an opportunity to ignore such cases. The filter function must be
// symmetric such that the filter result is identical regardless of whether the
// missing value is from x or y.
//
// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or
// a previously filtered [Option].
func FilterPath(f func(Path) bool, opt Option) Option {
if f == nil {
panic("invalid path filter function")
}
if opt := normalizeOption(opt); opt != nil {
return &pathFilter{fnc: f, opt: opt}
}
return nil
}
type pathFilter struct {
core
fnc func(Path) bool
opt Option
}
func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption {
if f.fnc(s.curPath) {
return f.opt.filter(s, t, vx, vy)
}
return nil
}
func (f pathFilter) String() string {
return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt)
}
// FilterValues returns a new [Option] where opt is only evaluated if filter f,
// which is a function of the form "func(T, T) bool", returns true for the
// current pair of values being compared. If either value is invalid or
// the type of the values is not assignable to T, then this filter implicitly
// returns false.
//
// The filter function must be
// symmetric (i.e., agnostic to the order of the inputs) and
// deterministic (i.e., produces the same result when given the same inputs).
// If T is an interface, it is possible that f is called with two values with
// different concrete types that both implement T.
//
// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or
// a previously filtered [Option].
func FilterValues(f interface{}, opt Option) Option {
v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() {
panic(fmt.Sprintf("invalid values filter function: %T", f))
}
if opt := normalizeOption(opt); opt != nil {
vf := &valuesFilter{fnc: v, opt: opt}
if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
vf.typ = ti
}
return vf
}
return nil
}
type valuesFilter struct {
core
typ reflect.Type // T
fnc reflect.Value // func(T, T) bool
opt Option
}
func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption {
if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() {
return nil
}
if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) {
return f.opt.filter(s, t, vx, vy)
}
return nil
}
func (f valuesFilter) String() string {
return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt)
}
// Ignore is an [Option] that causes all comparisons to be ignored.
// This value is intended to be combined with [FilterPath] or [FilterValues].
// It is an error to pass an unfiltered Ignore option to [Equal].
func Ignore() Option { return ignore{} }
type ignore struct{ core }
func (ignore) isFiltered() bool { return false }
func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} }
func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) }
func (ignore) String() string { return "Ignore()" }
// validator is a sentinel Option type to indicate that some options could not
// be evaluated due to unexported fields, missing slice elements, or
// missing map entries. Both values are validator only for unexported fields.
type validator struct{ core }
func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption {
if !vx.IsValid() || !vy.IsValid() {
return validator{}
}
if !vx.CanInterface() || !vy.CanInterface() {
return validator{}
}
return nil
}
func (validator) apply(s *state, vx, vy reflect.Value) {
// Implies missing slice element or map entry.
if !vx.IsValid() || !vy.IsValid() {
s.report(vx.IsValid() == vy.IsValid(), 0)
return
}
// Unable to Interface implies unexported field without visibility access.
if !vx.CanInterface() || !vy.CanInterface() {
help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported"
var name string
if t := s.curPath.Index(-2).Type(); t.Name() != "" {
// Named type with unexported fields.
name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType
if _, ok := reflect.New(t).Interface().(error); ok {
help = "consider using cmpopts.EquateErrors to compare error values"
} else if t.Comparable() {
help = "consider using cmpopts.EquateComparable to compare comparable Go types"
}
} else {
// Unnamed type with unexported fields. Derive PkgPath from field.
var pkgPath string
for i := 0; i < t.NumField() && pkgPath == ""; i++ {
pkgPath = t.Field(i).PkgPath
}
name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int })
}
panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help))
}
panic("not reachable")
}
// identRx represents a valid identifier according to the Go specification.
const identRx = `[_\p{L}][_\p{L}\p{N}]*`
var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`)
// Transformer returns an [Option] that applies a transformation function that
// converts values of a certain type into that of another.
//
// The transformer f must be a function "func(T) R" that converts values of
// type T to those of type R and is implicitly filtered to input values
// assignable to T. The transformer must not mutate T in any way.
//
// To help prevent some cases of infinite recursive cycles applying the
// same transform to the output of itself (e.g., in the case where the
// input and output types are the same), an implicit filter is added such that
// a transformer is applicable only if that exact transformer is not already
// in the tail of the [Path] since the last non-[Transform] step.
// For situations where the implicit filter is still insufficient,
// consider using [github.com/google/go-cmp/cmp/cmpopts.AcyclicTransformer],
// which adds a filter to prevent the transformer from
// being recursively applied upon itself.
//
// The name is a user provided label that is used as the [Transform.Name] in the
// transformation [PathStep] (and eventually shown in the [Diff] output).
// The name must be a valid identifier or qualified identifier in Go syntax.
// If empty, an arbitrary name is used.
func Transformer(name string, f interface{}) Option {
v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.Transformer) || v.IsNil() {
panic(fmt.Sprintf("invalid transformer function: %T", f))
}
if name == "" {
name = function.NameOf(v)
if !identsRx.MatchString(name) {
name = "λ" // Lambda-symbol as placeholder name
}
} else if !identsRx.MatchString(name) {
panic(fmt.Sprintf("invalid name: %q", name))
}
tr := &transformer{name: name, fnc: reflect.ValueOf(f)}
if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
tr.typ = ti
}
return tr
}
type transformer struct {
core
name string
typ reflect.Type // T
fnc reflect.Value // func(T) R
}
func (tr *transformer) isFiltered() bool { return tr.typ != nil }
func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption {
for i := len(s.curPath) - 1; i >= 0; i-- {
if t, ok := s.curPath[i].(Transform); !ok {
break // Hit most recent non-Transform step
} else if tr == t.trans {
return nil // Cannot directly use same Transform
}
}
if tr.typ == nil || t.AssignableTo(tr.typ) {
return tr
}
return nil
}
func (tr *transformer) apply(s *state, vx, vy reflect.Value) {
step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}}
vvx := s.callTRFunc(tr.fnc, vx, step)
vvy := s.callTRFunc(tr.fnc, vy, step)
step.vx, step.vy = vvx, vvy
s.compareAny(step)
}
func (tr transformer) String() string {
return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc))
}
// Comparer returns an [Option] that determines whether two values are equal
// to each other.
//
// The comparer f must be a function "func(T, T) bool" and is implicitly
// filtered to input values assignable to T. If T is an interface, it is
// possible that f is called with two values of different concrete types that
// both implement T.
//
// The equality function must be:
// - Symmetric: equal(x, y) == equal(y, x)
// - Deterministic: equal(x, y) == equal(x, y)
// - Pure: equal(x, y) does not modify x or y
func Comparer(f interface{}) Option {
v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.Equal) || v.IsNil() {
panic(fmt.Sprintf("invalid comparer function: %T", f))
}
cm := &comparer{fnc: v}
if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
cm.typ = ti
}
return cm
}
type comparer struct {
core
typ reflect.Type // T
fnc reflect.Value // func(T, T) bool
}
func (cm *comparer) isFiltered() bool { return cm.typ != nil }
func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption {
if cm.typ == nil || t.AssignableTo(cm.typ) {
return cm
}
return nil
}
func (cm *comparer) apply(s *state, vx, vy reflect.Value) {
eq := s.callTTBFunc(cm.fnc, vx, vy)
s.report(eq, reportByFunc)
}
func (cm comparer) String() string {
return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc))
}
// Exporter returns an [Option] that specifies whether [Equal] is allowed to
// introspect into the unexported fields of certain struct types.
//
// Users of this option must understand that comparing on unexported fields
// from external packages is not safe since changes in the internal
// implementation of some external package may cause the result of [Equal]
// to unexpectedly change. However, it may be valid to use this option on types
// defined in an internal package where the semantic meaning of an unexported
// field is in the control of the user.
//
// In many cases, a custom [Comparer] should be used instead that defines
// equality as a function of the public API of a type rather than the underlying
// unexported implementation.
//
// For example, the [reflect.Type] documentation defines equality to be determined
// by the == operator on the interface (essentially performing a shallow pointer
// comparison) and most attempts to compare *[regexp.Regexp] types are interested
// in only checking that the regular expression strings are equal.
// Both of these are accomplished using [Comparer] options:
//
// Comparer(func(x, y reflect.Type) bool { return x == y })
// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() })
//
// In other cases, the [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]
// option can be used to ignore all unexported fields on specified struct types.
func Exporter(f func(reflect.Type) bool) Option {
return exporter(f)
}
type exporter func(reflect.Type) bool
func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption {
panic("not implemented")
}
// AllowUnexported returns an [Option] that allows [Equal] to forcibly introspect
// unexported fields of the specified struct types.
//
// See [Exporter] for the proper use of this option.
func AllowUnexported(types ...interface{}) Option {
m := make(map[reflect.Type]bool)
for _, typ := range types {
t := reflect.TypeOf(typ)
if t.Kind() != reflect.Struct {
panic(fmt.Sprintf("invalid struct type: %T", typ))
}
m[t] = true
}
return exporter(func(t reflect.Type) bool { return m[t] })
}
// Result represents the comparison result for a single node and
// is provided by cmp when calling Report (see [Reporter]).
type Result struct {
_ [0]func() // Make Result incomparable
flags resultFlags
}
// Equal reports whether the node was determined to be equal or not.
// As a special case, ignored nodes are considered equal.
func (r Result) Equal() bool {
return r.flags&(reportEqual|reportByIgnore) != 0
}
// ByIgnore reports whether the node is equal because it was ignored.
// This never reports true if [Result.Equal] reports false.
func (r Result) ByIgnore() bool {
return r.flags&reportByIgnore != 0
}
// ByMethod reports whether the Equal method determined equality.
func (r Result) ByMethod() bool {
return r.flags&reportByMethod != 0
}
// ByFunc reports whether a [Comparer] function determined equality.
func (r Result) ByFunc() bool {
return r.flags&reportByFunc != 0
}
// ByCycle reports whether a reference cycle was detected.
func (r Result) ByCycle() bool {
return r.flags&reportByCycle != 0
}
type resultFlags uint
const (
_ resultFlags = (1 << iota) / 2
reportEqual
reportUnequal
reportByIgnore
reportByMethod
reportByFunc
reportByCycle
)
// Reporter is an [Option] that can be passed to [Equal]. When [Equal] traverses
// the value trees, it calls PushStep as it descends into each node in the
// tree and PopStep as it ascend out of the node. The leaves of the tree are
// either compared (determined to be equal or not equal) or ignored and reported
// as such by calling the Report method.
func Reporter(r interface {
// PushStep is called when a tree-traversal operation is performed.
// The PathStep itself is only valid until the step is popped.
// The PathStep.Values are valid for the duration of the entire traversal
// and must not be mutated.
//
// Equal always calls PushStep at the start to provide an operation-less
// PathStep used to report the root values.
//
// Within a slice, the exact set of inserted, removed, or modified elements
// is unspecified and may change in future implementations.
// The entries of a map are iterated through in an unspecified order.
PushStep(PathStep)
// Report is called exactly once on leaf nodes to report whether the
// comparison identified the node as equal, unequal, or ignored.
// A leaf node is one that is immediately preceded by and followed by
// a pair of PushStep and PopStep calls.
Report(Result)
// PopStep ascends back up the value tree.
// There is always a matching pop call for every push call.
PopStep()
}) Option {
return reporter{r}
}
type reporter struct{ reporterIface }
type reporterIface interface {
PushStep(PathStep)
Report(Result)
PopStep()
}
func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption {
panic("not implemented")
}
// normalizeOption normalizes the input options such that all Options groups
// are flattened and groups with a single element are reduced to that element.
// Only coreOptions and Options containing coreOptions are allowed.
func normalizeOption(src Option) Option {
switch opts := flattenOptions(nil, Options{src}); len(opts) {
case 0:
return nil
case 1:
return opts[0]
default:
return opts
}
}
// flattenOptions copies all options in src to dst as a flat list.
// Only coreOptions and Options containing coreOptions are allowed.
func flattenOptions(dst, src Options) Options {
for _, opt := range src {
switch opt := opt.(type) {
case nil:
continue
case Options:
dst = flattenOptions(dst, opt)
case coreOption:
dst = append(dst, opt)
default:
panic(fmt.Sprintf("invalid option type: %T", opt))
}
}
return dst
}

390
vendor/github.com/google/go-cmp/cmp/path.go generated vendored Normal file
View File

@@ -0,0 +1,390 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"fmt"
"reflect"
"strings"
"unicode"
"unicode/utf8"
"github.com/google/go-cmp/cmp/internal/value"
)
// Path is a list of [PathStep] describing the sequence of operations to get
// from some root type to the current position in the value tree.
// The first Path element is always an operation-less [PathStep] that exists
// simply to identify the initial type.
//
// When traversing structs with embedded structs, the embedded struct will
// always be accessed as a field before traversing the fields of the
// embedded struct themselves. That is, an exported field from the
// embedded struct will never be accessed directly from the parent struct.
type Path []PathStep
// PathStep is a union-type for specific operations to traverse
// a value's tree structure. Users of this package never need to implement
// these types as values of this type will be returned by this package.
//
// Implementations of this interface:
// - [StructField]
// - [SliceIndex]
// - [MapIndex]
// - [Indirect]
// - [TypeAssertion]
// - [Transform]
type PathStep interface {
String() string
// Type is the resulting type after performing the path step.
Type() reflect.Type
// Values is the resulting values after performing the path step.
// The type of each valid value is guaranteed to be identical to Type.
//
// In some cases, one or both may be invalid or have restrictions:
// - For StructField, both are not interface-able if the current field
// is unexported and the struct type is not explicitly permitted by
// an Exporter to traverse unexported fields.
// - For SliceIndex, one may be invalid if an element is missing from
// either the x or y slice.
// - For MapIndex, one may be invalid if an entry is missing from
// either the x or y map.
//
// The provided values must not be mutated.
Values() (vx, vy reflect.Value)
}
var (
_ PathStep = StructField{}
_ PathStep = SliceIndex{}
_ PathStep = MapIndex{}
_ PathStep = Indirect{}
_ PathStep = TypeAssertion{}
_ PathStep = Transform{}
)
func (pa *Path) push(s PathStep) {
*pa = append(*pa, s)
}
func (pa *Path) pop() {
*pa = (*pa)[:len(*pa)-1]
}
// Last returns the last [PathStep] in the Path.
// If the path is empty, this returns a non-nil [PathStep]
// that reports a nil [PathStep.Type].
func (pa Path) Last() PathStep {
return pa.Index(-1)
}
// Index returns the ith step in the Path and supports negative indexing.
// A negative index starts counting from the tail of the Path such that -1
// refers to the last step, -2 refers to the second-to-last step, and so on.
// If index is invalid, this returns a non-nil [PathStep]
// that reports a nil [PathStep.Type].
func (pa Path) Index(i int) PathStep {
if i < 0 {
i = len(pa) + i
}
if i < 0 || i >= len(pa) {
return pathStep{}
}
return pa[i]
}
// String returns the simplified path to a node.
// The simplified path only contains struct field accesses.
//
// For example:
//
// MyMap.MySlices.MyField
func (pa Path) String() string {
var ss []string
for _, s := range pa {
if _, ok := s.(StructField); ok {
ss = append(ss, s.String())
}
}
return strings.TrimPrefix(strings.Join(ss, ""), ".")
}
// GoString returns the path to a specific node using Go syntax.
//
// For example:
//
// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField
func (pa Path) GoString() string {
var ssPre, ssPost []string
var numIndirect int
for i, s := range pa {
var nextStep PathStep
if i+1 < len(pa) {
nextStep = pa[i+1]
}
switch s := s.(type) {
case Indirect:
numIndirect++
pPre, pPost := "(", ")"
switch nextStep.(type) {
case Indirect:
continue // Next step is indirection, so let them batch up
case StructField:
numIndirect-- // Automatic indirection on struct fields
case nil:
pPre, pPost = "", "" // Last step; no need for parenthesis
}
if numIndirect > 0 {
ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect))
ssPost = append(ssPost, pPost)
}
numIndirect = 0
continue
case Transform:
ssPre = append(ssPre, s.trans.name+"(")
ssPost = append(ssPost, ")")
continue
}
ssPost = append(ssPost, s.String())
}
for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 {
ssPre[i], ssPre[j] = ssPre[j], ssPre[i]
}
return strings.Join(ssPre, "") + strings.Join(ssPost, "")
}
type pathStep struct {
typ reflect.Type
vx, vy reflect.Value
}
func (ps pathStep) Type() reflect.Type { return ps.typ }
func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy }
func (ps pathStep) String() string {
if ps.typ == nil {
return "<nil>"
}
s := value.TypeString(ps.typ, false)
if s == "" || strings.ContainsAny(s, "{}\n") {
return "root" // Type too simple or complex to print
}
return fmt.Sprintf("{%s}", s)
}
// StructField is a [PathStep] that represents a struct field access
// on a field called [StructField.Name].
type StructField struct{ *structField }
type structField struct {
pathStep
name string
idx int
// These fields are used for forcibly accessing an unexported field.
// pvx, pvy, and field are only valid if unexported is true.
unexported bool
mayForce bool // Forcibly allow visibility
paddr bool // Was parent addressable?
pvx, pvy reflect.Value // Parent values (always addressable)
field reflect.StructField // Field information
}
func (sf StructField) Type() reflect.Type { return sf.typ }
func (sf StructField) Values() (vx, vy reflect.Value) {
if !sf.unexported {
return sf.vx, sf.vy // CanInterface reports true
}
// Forcibly obtain read-write access to an unexported struct field.
if sf.mayForce {
vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr)
vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr)
return vx, vy // CanInterface reports true
}
return sf.vx, sf.vy // CanInterface reports false
}
func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) }
// Name is the field name.
func (sf StructField) Name() string { return sf.name }
// Index is the index of the field in the parent struct type.
// See [reflect.Type.Field].
func (sf StructField) Index() int { return sf.idx }
// SliceIndex is a [PathStep] that represents an index operation on
// a slice or array at some index [SliceIndex.Key].
type SliceIndex struct{ *sliceIndex }
type sliceIndex struct {
pathStep
xkey, ykey int
isSlice bool // False for reflect.Array
}
func (si SliceIndex) Type() reflect.Type { return si.typ }
func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy }
func (si SliceIndex) String() string {
switch {
case si.xkey == si.ykey:
return fmt.Sprintf("[%d]", si.xkey)
case si.ykey == -1:
// [5->?] means "I don't know where X[5] went"
return fmt.Sprintf("[%d->?]", si.xkey)
case si.xkey == -1:
// [?->3] means "I don't know where Y[3] came from"
return fmt.Sprintf("[?->%d]", si.ykey)
default:
// [5->3] means "X[5] moved to Y[3]"
return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
}
}
// Key is the index key; it may return -1 if in a split state
func (si SliceIndex) Key() int {
if si.xkey != si.ykey {
return -1
}
return si.xkey
}
// SplitKeys are the indexes for indexing into slices in the
// x and y values, respectively. These indexes may differ due to the
// insertion or removal of an element in one of the slices, causing
// all of the indexes to be shifted. If an index is -1, then that
// indicates that the element does not exist in the associated slice.
//
// [SliceIndex.Key] is guaranteed to return -1 if and only if the indexes
// returned by SplitKeys are not the same. SplitKeys will never return -1 for
// both indexes.
func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey }
// MapIndex is a [PathStep] that represents an index operation on a map at some index Key.
type MapIndex struct{ *mapIndex }
type mapIndex struct {
pathStep
key reflect.Value
}
func (mi MapIndex) Type() reflect.Type { return mi.typ }
func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy }
func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
// Key is the value of the map key.
func (mi MapIndex) Key() reflect.Value { return mi.key }
// Indirect is a [PathStep] that represents pointer indirection on the parent type.
type Indirect struct{ *indirect }
type indirect struct {
pathStep
}
func (in Indirect) Type() reflect.Type { return in.typ }
func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy }
func (in Indirect) String() string { return "*" }
// TypeAssertion is a [PathStep] that represents a type assertion on an interface.
type TypeAssertion struct{ *typeAssertion }
type typeAssertion struct {
pathStep
}
func (ta TypeAssertion) Type() reflect.Type { return ta.typ }
func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy }
func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) }
// Transform is a [PathStep] that represents a transformation
// from the parent type to the current type.
type Transform struct{ *transform }
type transform struct {
pathStep
trans *transformer
}
func (tf Transform) Type() reflect.Type { return tf.typ }
func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy }
func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
// Name is the name of the [Transformer].
func (tf Transform) Name() string { return tf.trans.name }
// Func is the function pointer to the transformer function.
func (tf Transform) Func() reflect.Value { return tf.trans.fnc }
// Option returns the originally constructed [Transformer] option.
// The == operator can be used to detect the exact option used.
func (tf Transform) Option() Option { return tf.trans }
// pointerPath represents a dual-stack of pointers encountered when
// recursively traversing the x and y values. This data structure supports
// detection of cycles and determining whether the cycles are equal.
// In Go, cycles can occur via pointers, slices, and maps.
//
// The pointerPath uses a map to represent a stack; where descension into a
// pointer pushes the address onto the stack, and ascension from a pointer
// pops the address from the stack. Thus, when traversing into a pointer from
// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles
// by checking whether the pointer has already been visited. The cycle detection
// uses a separate stack for the x and y values.
//
// If a cycle is detected we need to determine whether the two pointers
// should be considered equal. The definition of equality chosen by Equal
// requires two graphs to have the same structure. To determine this, both the
// x and y values must have a cycle where the previous pointers were also
// encountered together as a pair.
//
// Semantically, this is equivalent to augmenting Indirect, SliceIndex, and
// MapIndex with pointer information for the x and y values.
// Suppose px and py are two pointers to compare, we then search the
// Path for whether px was ever encountered in the Path history of x, and
// similarly so with py. If either side has a cycle, the comparison is only
// equal if both px and py have a cycle resulting from the same PathStep.
//
// Using a map as a stack is more performant as we can perform cycle detection
// in O(1) instead of O(N) where N is len(Path).
type pointerPath struct {
// mx is keyed by x pointers, where the value is the associated y pointer.
mx map[value.Pointer]value.Pointer
// my is keyed by y pointers, where the value is the associated x pointer.
my map[value.Pointer]value.Pointer
}
func (p *pointerPath) Init() {
p.mx = make(map[value.Pointer]value.Pointer)
p.my = make(map[value.Pointer]value.Pointer)
}
// Push indicates intent to descend into pointers vx and vy where
// visited reports whether either has been seen before. If visited before,
// equal reports whether both pointers were encountered together.
// Pop must be called if and only if the pointers were never visited.
//
// The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map
// and be non-nil.
func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) {
px := value.PointerOf(vx)
py := value.PointerOf(vy)
_, ok1 := p.mx[px]
_, ok2 := p.my[py]
if ok1 || ok2 {
equal = p.mx[px] == py && p.my[py] == px // Pointers paired together
return equal, true
}
p.mx[px] = py
p.my[py] = px
return false, false
}
// Pop ascends from pointers vx and vy.
func (p pointerPath) Pop(vx, vy reflect.Value) {
delete(p.mx, value.PointerOf(vx))
delete(p.my, value.PointerOf(vy))
}
// isExported reports whether the identifier is exported.
func isExported(id string) bool {
r, _ := utf8.DecodeRuneInString(id)
return unicode.IsUpper(r)
}

54
vendor/github.com/google/go-cmp/cmp/report.go generated vendored Normal file
View File

@@ -0,0 +1,54 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
// defaultReporter implements the reporter interface.
//
// As Equal serially calls the PushStep, Report, and PopStep methods, the
// defaultReporter constructs a tree-based representation of the compared value
// and the result of each comparison (see valueNode).
//
// When the String method is called, the FormatDiff method transforms the
// valueNode tree into a textNode tree, which is a tree-based representation
// of the textual output (see textNode).
//
// Lastly, the textNode.String method produces the final report as a string.
type defaultReporter struct {
root *valueNode
curr *valueNode
}
func (r *defaultReporter) PushStep(ps PathStep) {
r.curr = r.curr.PushStep(ps)
if r.root == nil {
r.root = r.curr
}
}
func (r *defaultReporter) Report(rs Result) {
r.curr.Report(rs)
}
func (r *defaultReporter) PopStep() {
r.curr = r.curr.PopStep()
}
// String provides a full report of the differences detected as a structured
// literal in pseudo-Go syntax. String may only be called after the entire tree
// has been traversed.
func (r *defaultReporter) String() string {
assert(r.root != nil && r.curr == nil)
if r.root.NumDiff == 0 {
return ""
}
ptrs := new(pointerReferences)
text := formatOptions{}.FormatDiff(r.root, ptrs)
resolveReferences(text)
return text.String()
}
func assert(ok bool) {
if !ok {
panic("assertion failure")
}
}

433
vendor/github.com/google/go-cmp/cmp/report_compare.go generated vendored Normal file
View File

@@ -0,0 +1,433 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"fmt"
"reflect"
)
// numContextRecords is the number of surrounding equal records to print.
const numContextRecords = 2
type diffMode byte
const (
diffUnknown diffMode = 0
diffIdentical diffMode = ' '
diffRemoved diffMode = '-'
diffInserted diffMode = '+'
)
type typeMode int
const (
// emitType always prints the type.
emitType typeMode = iota
// elideType never prints the type.
elideType
// autoType prints the type only for composite kinds
// (i.e., structs, slices, arrays, and maps).
autoType
)
type formatOptions struct {
// DiffMode controls the output mode of FormatDiff.
//
// If diffUnknown, then produce a diff of the x and y values.
// If diffIdentical, then emit values as if they were equal.
// If diffRemoved, then only emit x values (ignoring y values).
// If diffInserted, then only emit y values (ignoring x values).
DiffMode diffMode
// TypeMode controls whether to print the type for the current node.
//
// As a general rule of thumb, we always print the type of the next node
// after an interface, and always elide the type of the next node after
// a slice or map node.
TypeMode typeMode
// formatValueOptions are options specific to printing reflect.Values.
formatValueOptions
}
func (opts formatOptions) WithDiffMode(d diffMode) formatOptions {
opts.DiffMode = d
return opts
}
func (opts formatOptions) WithTypeMode(t typeMode) formatOptions {
opts.TypeMode = t
return opts
}
func (opts formatOptions) WithVerbosity(level int) formatOptions {
opts.VerbosityLevel = level
opts.LimitVerbosity = true
return opts
}
func (opts formatOptions) verbosity() uint {
switch {
case opts.VerbosityLevel < 0:
return 0
case opts.VerbosityLevel > 16:
return 16 // some reasonable maximum to avoid shift overflow
default:
return uint(opts.VerbosityLevel)
}
}
const maxVerbosityPreset = 6
// verbosityPreset modifies the verbosity settings given an index
// between 0 and maxVerbosityPreset, inclusive.
func verbosityPreset(opts formatOptions, i int) formatOptions {
opts.VerbosityLevel = int(opts.verbosity()) + 2*i
if i > 0 {
opts.AvoidStringer = true
}
if i >= maxVerbosityPreset {
opts.PrintAddresses = true
opts.QualifiedNames = true
}
return opts
}
// FormatDiff converts a valueNode tree into a textNode tree, where the later
// is a textual representation of the differences detected in the former.
func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) {
if opts.DiffMode == diffIdentical {
opts = opts.WithVerbosity(1)
} else if opts.verbosity() < 3 {
opts = opts.WithVerbosity(3)
}
// Check whether we have specialized formatting for this node.
// This is not necessary, but helpful for producing more readable outputs.
if opts.CanFormatDiffSlice(v) {
return opts.FormatDiffSlice(v)
}
var parentKind reflect.Kind
if v.parent != nil && v.parent.TransformerName == "" {
parentKind = v.parent.Type.Kind()
}
// For leaf nodes, format the value based on the reflect.Values alone.
// As a special case, treat equal []byte as a leaf nodes.
isBytes := v.Type.Kind() == reflect.Slice && v.Type.Elem() == byteType
isEqualBytes := isBytes && v.NumDiff+v.NumIgnored+v.NumTransformed == 0
if v.MaxDepth == 0 || isEqualBytes {
switch opts.DiffMode {
case diffUnknown, diffIdentical:
// Format Equal.
if v.NumDiff == 0 {
outx := opts.FormatValue(v.ValueX, parentKind, ptrs)
outy := opts.FormatValue(v.ValueY, parentKind, ptrs)
if v.NumIgnored > 0 && v.NumSame == 0 {
return textEllipsis
} else if outx.Len() < outy.Len() {
return outx
} else {
return outy
}
}
// Format unequal.
assert(opts.DiffMode == diffUnknown)
var list textList
outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs)
outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs)
for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ {
opts2 := verbosityPreset(opts, i).WithTypeMode(elideType)
outx = opts2.FormatValue(v.ValueX, parentKind, ptrs)
outy = opts2.FormatValue(v.ValueY, parentKind, ptrs)
}
if outx != nil {
list = append(list, textRecord{Diff: '-', Value: outx})
}
if outy != nil {
list = append(list, textRecord{Diff: '+', Value: outy})
}
return opts.WithTypeMode(emitType).FormatType(v.Type, list)
case diffRemoved:
return opts.FormatValue(v.ValueX, parentKind, ptrs)
case diffInserted:
return opts.FormatValue(v.ValueY, parentKind, ptrs)
default:
panic("invalid diff mode")
}
}
// Register slice element to support cycle detection.
if parentKind == reflect.Slice {
ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true)
defer ptrs.Pop()
defer func() { out = wrapTrunkReferences(ptrRefs, out) }()
}
// Descend into the child value node.
if v.TransformerName != "" {
out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"}
return opts.FormatType(v.Type, out)
} else {
switch k := v.Type.Kind(); k {
case reflect.Struct, reflect.Array, reflect.Slice:
out = opts.formatDiffList(v.Records, k, ptrs)
out = opts.FormatType(v.Type, out)
case reflect.Map:
// Register map to support cycle detection.
ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false)
defer ptrs.Pop()
out = opts.formatDiffList(v.Records, k, ptrs)
out = wrapTrunkReferences(ptrRefs, out)
out = opts.FormatType(v.Type, out)
case reflect.Ptr:
// Register pointer to support cycle detection.
ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false)
defer ptrs.Pop()
out = opts.FormatDiff(v.Value, ptrs)
out = wrapTrunkReferences(ptrRefs, out)
out = &textWrap{Prefix: "&", Value: out}
case reflect.Interface:
out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
default:
panic(fmt.Sprintf("%v cannot have children", k))
}
return out
}
}
func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode {
// Derive record name based on the data structure kind.
var name string
var formatKey func(reflect.Value) string
switch k {
case reflect.Struct:
name = "field"
opts = opts.WithTypeMode(autoType)
formatKey = func(v reflect.Value) string { return v.String() }
case reflect.Slice, reflect.Array:
name = "element"
opts = opts.WithTypeMode(elideType)
formatKey = func(reflect.Value) string { return "" }
case reflect.Map:
name = "entry"
opts = opts.WithTypeMode(elideType)
formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) }
}
maxLen := -1
if opts.LimitVerbosity {
if opts.DiffMode == diffIdentical {
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
} else {
maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc...
}
opts.VerbosityLevel--
}
// Handle unification.
switch opts.DiffMode {
case diffIdentical, diffRemoved, diffInserted:
var list textList
var deferredEllipsis bool // Add final "..." to indicate records were dropped
for _, r := range recs {
if len(list) == maxLen {
deferredEllipsis = true
break
}
// Elide struct fields that are zero value.
if k == reflect.Struct {
var isZero bool
switch opts.DiffMode {
case diffIdentical:
isZero = r.Value.ValueX.IsZero() || r.Value.ValueY.IsZero()
case diffRemoved:
isZero = r.Value.ValueX.IsZero()
case diffInserted:
isZero = r.Value.ValueY.IsZero()
}
if isZero {
continue
}
}
// Elide ignored nodes.
if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 {
deferredEllipsis = !(k == reflect.Slice || k == reflect.Array)
if !deferredEllipsis {
list.AppendEllipsis(diffStats{})
}
continue
}
if out := opts.FormatDiff(r.Value, ptrs); out != nil {
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
}
}
if deferredEllipsis {
list.AppendEllipsis(diffStats{})
}
return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
case diffUnknown:
default:
panic("invalid diff mode")
}
// Handle differencing.
var numDiffs int
var list textList
var keys []reflect.Value // invariant: len(list) == len(keys)
groups := coalesceAdjacentRecords(name, recs)
maxGroup := diffStats{Name: name}
for i, ds := range groups {
if maxLen >= 0 && numDiffs >= maxLen {
maxGroup = maxGroup.Append(ds)
continue
}
// Handle equal records.
if ds.NumDiff() == 0 {
// Compute the number of leading and trailing records to print.
var numLo, numHi int
numEqual := ds.NumIgnored + ds.NumIdentical
for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 {
if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 {
break
}
numLo++
}
for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 {
if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 {
break
}
numHi++
}
if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 {
numHi++ // Avoid pointless coalescing of a single equal record
}
// Format the equal values.
for _, r := range recs[:numLo] {
out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs)
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
keys = append(keys, r.Key)
}
if numEqual > numLo+numHi {
ds.NumIdentical -= numLo + numHi
list.AppendEllipsis(ds)
for len(keys) < len(list) {
keys = append(keys, reflect.Value{})
}
}
for _, r := range recs[numEqual-numHi : numEqual] {
out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs)
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
keys = append(keys, r.Key)
}
recs = recs[numEqual:]
continue
}
// Handle unequal records.
for _, r := range recs[:ds.NumDiff()] {
switch {
case opts.CanFormatDiffSlice(r.Value):
out := opts.FormatDiffSlice(r.Value)
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
keys = append(keys, r.Key)
case r.Value.NumChildren == r.Value.MaxDepth:
outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs)
outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs)
for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ {
opts2 := verbosityPreset(opts, i)
outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs)
outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs)
}
if outx != nil {
list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx})
keys = append(keys, r.Key)
}
if outy != nil {
list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy})
keys = append(keys, r.Key)
}
default:
out := opts.FormatDiff(r.Value, ptrs)
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
keys = append(keys, r.Key)
}
}
recs = recs[ds.NumDiff():]
numDiffs += ds.NumDiff()
}
if maxGroup.IsZero() {
assert(len(recs) == 0)
} else {
list.AppendEllipsis(maxGroup)
for len(keys) < len(list) {
keys = append(keys, reflect.Value{})
}
}
assert(len(list) == len(keys))
// For maps, the default formatting logic uses fmt.Stringer which may
// produce ambiguous output. Avoid calling String to disambiguate.
if k == reflect.Map {
var ambiguous bool
seenKeys := map[string]reflect.Value{}
for i, currKey := range keys {
if currKey.IsValid() {
strKey := list[i].Key
prevKey, seen := seenKeys[strKey]
if seen && prevKey.CanInterface() && currKey.CanInterface() {
ambiguous = prevKey.Interface() != currKey.Interface()
if ambiguous {
break
}
}
seenKeys[strKey] = currKey
}
}
if ambiguous {
for i, k := range keys {
if k.IsValid() {
list[i].Key = formatMapKey(k, true, ptrs)
}
}
}
}
return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
}
// coalesceAdjacentRecords coalesces the list of records into groups of
// adjacent equal, or unequal counts.
func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) {
var prevCase int // Arbitrary index into which case last occurred
lastStats := func(i int) *diffStats {
if prevCase != i {
groups = append(groups, diffStats{Name: name})
prevCase = i
}
return &groups[len(groups)-1]
}
for _, r := range recs {
switch rv := r.Value; {
case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0:
lastStats(1).NumIgnored++
case rv.NumDiff == 0:
lastStats(1).NumIdentical++
case rv.NumDiff > 0 && !rv.ValueY.IsValid():
lastStats(2).NumRemoved++
case rv.NumDiff > 0 && !rv.ValueX.IsValid():
lastStats(2).NumInserted++
default:
lastStats(2).NumModified++
}
}
return groups
}

View File

@@ -0,0 +1,264 @@
// Copyright 2020, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"fmt"
"reflect"
"strings"
"github.com/google/go-cmp/cmp/internal/flags"
"github.com/google/go-cmp/cmp/internal/value"
)
const (
pointerDelimPrefix = "⟪"
pointerDelimSuffix = "⟫"
)
// formatPointer prints the address of the pointer.
func formatPointer(p value.Pointer, withDelims bool) string {
v := p.Uintptr()
if flags.Deterministic {
v = 0xdeadf00f // Only used for stable testing purposes
}
if withDelims {
return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix
}
return formatHex(uint64(v))
}
// pointerReferences is a stack of pointers visited so far.
type pointerReferences [][2]value.Pointer
func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) {
if deref && vx.IsValid() {
vx = vx.Addr()
}
if deref && vy.IsValid() {
vy = vy.Addr()
}
switch d {
case diffUnknown, diffIdentical:
pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)}
case diffRemoved:
pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}}
case diffInserted:
pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)}
}
*ps = append(*ps, pp)
return pp
}
func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) {
p = value.PointerOf(v)
for _, pp := range *ps {
if p == pp[0] || p == pp[1] {
return p, true
}
}
*ps = append(*ps, [2]value.Pointer{p, p})
return p, false
}
func (ps *pointerReferences) Pop() {
*ps = (*ps)[:len(*ps)-1]
}
// trunkReferences is metadata for a textNode indicating that the sub-tree
// represents the value for either pointer in a pair of references.
type trunkReferences struct{ pp [2]value.Pointer }
// trunkReference is metadata for a textNode indicating that the sub-tree
// represents the value for the given pointer reference.
type trunkReference struct{ p value.Pointer }
// leafReference is metadata for a textNode indicating that the value is
// truncated as it refers to another part of the tree (i.e., a trunk).
type leafReference struct{ p value.Pointer }
func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode {
switch {
case pp[0].IsNil():
return &textWrap{Value: s, Metadata: trunkReference{pp[1]}}
case pp[1].IsNil():
return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
case pp[0] == pp[1]:
return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
default:
return &textWrap{Value: s, Metadata: trunkReferences{pp}}
}
}
func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode {
var prefix string
if printAddress {
prefix = formatPointer(p, true)
}
return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}}
}
func makeLeafReference(p value.Pointer, printAddress bool) textNode {
out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"}
var prefix string
if printAddress {
prefix = formatPointer(p, true)
}
return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}}
}
// resolveReferences walks the textNode tree searching for any leaf reference
// metadata and resolves each against the corresponding trunk references.
// Since pointer addresses in memory are not particularly readable to the user,
// it replaces each pointer value with an arbitrary and unique reference ID.
func resolveReferences(s textNode) {
var walkNodes func(textNode, func(textNode))
walkNodes = func(s textNode, f func(textNode)) {
f(s)
switch s := s.(type) {
case *textWrap:
walkNodes(s.Value, f)
case textList:
for _, r := range s {
walkNodes(r.Value, f)
}
}
}
// Collect all trunks and leaves with reference metadata.
var trunks, leaves []*textWrap
walkNodes(s, func(s textNode) {
if s, ok := s.(*textWrap); ok {
switch s.Metadata.(type) {
case leafReference:
leaves = append(leaves, s)
case trunkReference, trunkReferences:
trunks = append(trunks, s)
}
}
})
// No leaf references to resolve.
if len(leaves) == 0 {
return
}
// Collect the set of all leaf references to resolve.
leafPtrs := make(map[value.Pointer]bool)
for _, leaf := range leaves {
leafPtrs[leaf.Metadata.(leafReference).p] = true
}
// Collect the set of trunk pointers that are always paired together.
// This allows us to assign a single ID to both pointers for brevity.
// If a pointer in a pair ever occurs by itself or as a different pair,
// then the pair is broken.
pairedTrunkPtrs := make(map[value.Pointer]value.Pointer)
unpair := func(p value.Pointer) {
if !pairedTrunkPtrs[p].IsNil() {
pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half
}
pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half
}
for _, trunk := range trunks {
switch p := trunk.Metadata.(type) {
case trunkReference:
unpair(p.p) // standalone pointer cannot be part of a pair
case trunkReferences:
p0, ok0 := pairedTrunkPtrs[p.pp[0]]
p1, ok1 := pairedTrunkPtrs[p.pp[1]]
switch {
case !ok0 && !ok1:
// Register the newly seen pair.
pairedTrunkPtrs[p.pp[0]] = p.pp[1]
pairedTrunkPtrs[p.pp[1]] = p.pp[0]
case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]:
// Exact pair already seen; do nothing.
default:
// Pair conflicts with some other pair; break all pairs.
unpair(p.pp[0])
unpair(p.pp[1])
}
}
}
// Correlate each pointer referenced by leaves to a unique identifier,
// and print the IDs for each trunk that matches those pointers.
var nextID uint
ptrIDs := make(map[value.Pointer]uint)
newID := func() uint {
id := nextID
nextID++
return id
}
for _, trunk := range trunks {
switch p := trunk.Metadata.(type) {
case trunkReference:
if print := leafPtrs[p.p]; print {
id, ok := ptrIDs[p.p]
if !ok {
id = newID()
ptrIDs[p.p] = id
}
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
}
case trunkReferences:
print0 := leafPtrs[p.pp[0]]
print1 := leafPtrs[p.pp[1]]
if print0 || print1 {
id0, ok0 := ptrIDs[p.pp[0]]
id1, ok1 := ptrIDs[p.pp[1]]
isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0]
if isPair {
var id uint
assert(ok0 == ok1) // must be seen together or not at all
if ok0 {
assert(id0 == id1) // must have the same ID
id = id0
} else {
id = newID()
ptrIDs[p.pp[0]] = id
ptrIDs[p.pp[1]] = id
}
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
} else {
if print0 && !ok0 {
id0 = newID()
ptrIDs[p.pp[0]] = id0
}
if print1 && !ok1 {
id1 = newID()
ptrIDs[p.pp[1]] = id1
}
switch {
case print0 && print1:
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1))
case print0:
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0))
case print1:
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1))
}
}
}
}
}
// Update all leaf references with the unique identifier.
for _, leaf := range leaves {
if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok {
leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id))
}
}
}
func formatReference(id uint) string {
return fmt.Sprintf("ref#%d", id)
}
func updateReferencePrefix(prefix, ref string) string {
if prefix == "" {
return pointerDelimPrefix + ref + pointerDelimSuffix
}
suffix := strings.TrimPrefix(prefix, pointerDelimPrefix)
return pointerDelimPrefix + ref + ": " + suffix
}

414
vendor/github.com/google/go-cmp/cmp/report_reflect.go generated vendored Normal file
View File

@@ -0,0 +1,414 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"bytes"
"fmt"
"reflect"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/google/go-cmp/cmp/internal/value"
)
var (
anyType = reflect.TypeOf((*interface{})(nil)).Elem()
stringType = reflect.TypeOf((*string)(nil)).Elem()
bytesType = reflect.TypeOf((*[]byte)(nil)).Elem()
byteType = reflect.TypeOf((*byte)(nil)).Elem()
)
type formatValueOptions struct {
// AvoidStringer controls whether to avoid calling custom stringer
// methods like error.Error or fmt.Stringer.String.
AvoidStringer bool
// PrintAddresses controls whether to print the address of all pointers,
// slice elements, and maps.
PrintAddresses bool
// QualifiedNames controls whether FormatType uses the fully qualified name
// (including the full package path as opposed to just the package name).
QualifiedNames bool
// VerbosityLevel controls the amount of output to produce.
// A higher value produces more output. A value of zero or lower produces
// no output (represented using an ellipsis).
// If LimitVerbosity is false, then the level is treated as infinite.
VerbosityLevel int
// LimitVerbosity specifies that formatting should respect VerbosityLevel.
LimitVerbosity bool
}
// FormatType prints the type as if it were wrapping s.
// This may return s as-is depending on the current type and TypeMode mode.
func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
// Check whether to emit the type or not.
switch opts.TypeMode {
case autoType:
switch t.Kind() {
case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
if s.Equal(textNil) {
return s
}
default:
return s
}
if opts.DiffMode == diffIdentical {
return s // elide type for identical nodes
}
case elideType:
return s
}
// Determine the type label, applying special handling for unnamed types.
typeName := value.TypeString(t, opts.QualifiedNames)
if t.Name() == "" {
// According to Go grammar, certain type literals contain symbols that
// do not strongly bind to the next lexicographical token (e.g., *T).
switch t.Kind() {
case reflect.Chan, reflect.Func, reflect.Ptr:
typeName = "(" + typeName + ")"
}
}
return &textWrap{Prefix: typeName, Value: wrapParens(s)}
}
// wrapParens wraps s with a set of parenthesis, but avoids it if the
// wrapped node itself is already surrounded by a pair of parenthesis or braces.
// It handles unwrapping one level of pointer-reference nodes.
func wrapParens(s textNode) textNode {
var refNode *textWrap
if s2, ok := s.(*textWrap); ok {
// Unwrap a single pointer reference node.
switch s2.Metadata.(type) {
case leafReference, trunkReference, trunkReferences:
refNode = s2
if s3, ok := refNode.Value.(*textWrap); ok {
s2 = s3
}
}
// Already has delimiters that make parenthesis unnecessary.
hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")")
hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}")
if hasParens || hasBraces {
return s
}
}
if refNode != nil {
refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"}
return s
}
return &textWrap{Prefix: "(", Value: s, Suffix: ")"}
}
// FormatValue prints the reflect.Value, taking extra care to avoid descending
// into pointers already in ptrs. As pointers are visited, ptrs is also updated.
func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) {
if !v.IsValid() {
return nil
}
t := v.Type()
// Check slice element for cycles.
if parentKind == reflect.Slice {
ptrRef, visited := ptrs.Push(v.Addr())
if visited {
return makeLeafReference(ptrRef, false)
}
defer ptrs.Pop()
defer func() { out = wrapTrunkReference(ptrRef, false, out) }()
}
// Check whether there is an Error or String method to call.
if !opts.AvoidStringer && v.CanInterface() {
// Avoid calling Error or String methods on nil receivers since many
// implementations crash when doing so.
if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
var prefix, strVal string
func() {
// Swallow and ignore any panics from String or Error.
defer func() { recover() }()
switch v := v.Interface().(type) {
case error:
strVal = v.Error()
prefix = "e"
case fmt.Stringer:
strVal = v.String()
prefix = "s"
}
}()
if prefix != "" {
return opts.formatString(prefix, strVal)
}
}
}
// Check whether to explicitly wrap the result with the type.
var skipType bool
defer func() {
if !skipType {
out = opts.FormatType(t, out)
}
}()
switch t.Kind() {
case reflect.Bool:
return textLine(fmt.Sprint(v.Bool()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return textLine(fmt.Sprint(v.Int()))
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return textLine(fmt.Sprint(v.Uint()))
case reflect.Uint8:
if parentKind == reflect.Slice || parentKind == reflect.Array {
return textLine(formatHex(v.Uint()))
}
return textLine(fmt.Sprint(v.Uint()))
case reflect.Uintptr:
return textLine(formatHex(v.Uint()))
case reflect.Float32, reflect.Float64:
return textLine(fmt.Sprint(v.Float()))
case reflect.Complex64, reflect.Complex128:
return textLine(fmt.Sprint(v.Complex()))
case reflect.String:
return opts.formatString("", v.String())
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
return textLine(formatPointer(value.PointerOf(v), true))
case reflect.Struct:
var list textList
v := makeAddressable(v) // needed for retrieveUnexportedField
maxLen := v.NumField()
if opts.LimitVerbosity {
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
opts.VerbosityLevel--
}
for i := 0; i < v.NumField(); i++ {
vv := v.Field(i)
if vv.IsZero() {
continue // Elide fields with zero values
}
if len(list) == maxLen {
list.AppendEllipsis(diffStats{})
break
}
sf := t.Field(i)
if !isExported(sf.Name) {
vv = retrieveUnexportedField(v, sf, true)
}
s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs)
list = append(list, textRecord{Key: sf.Name, Value: s})
}
return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
case reflect.Slice:
if v.IsNil() {
return textNil
}
// Check whether this is a []byte of text data.
if t.Elem() == byteType {
b := v.Bytes()
isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) || unicode.IsSpace(r) }
if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 {
out = opts.formatString("", string(b))
skipType = true
return opts.FormatType(t, out)
}
}
fallthrough
case reflect.Array:
maxLen := v.Len()
if opts.LimitVerbosity {
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
opts.VerbosityLevel--
}
var list textList
for i := 0; i < v.Len(); i++ {
if len(list) == maxLen {
list.AppendEllipsis(diffStats{})
break
}
s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs)
list = append(list, textRecord{Value: s})
}
out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
if t.Kind() == reflect.Slice && opts.PrintAddresses {
header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap())
out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out}
}
return out
case reflect.Map:
if v.IsNil() {
return textNil
}
// Check pointer for cycles.
ptrRef, visited := ptrs.Push(v)
if visited {
return makeLeafReference(ptrRef, opts.PrintAddresses)
}
defer ptrs.Pop()
maxLen := v.Len()
if opts.LimitVerbosity {
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
opts.VerbosityLevel--
}
var list textList
for _, k := range value.SortKeys(v.MapKeys()) {
if len(list) == maxLen {
list.AppendEllipsis(diffStats{})
break
}
sk := formatMapKey(k, false, ptrs)
sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs)
list = append(list, textRecord{Key: sk, Value: sv})
}
out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
return out
case reflect.Ptr:
if v.IsNil() {
return textNil
}
// Check pointer for cycles.
ptrRef, visited := ptrs.Push(v)
if visited {
out = makeLeafReference(ptrRef, opts.PrintAddresses)
return &textWrap{Prefix: "&", Value: out}
}
defer ptrs.Pop()
// Skip the name only if this is an unnamed pointer type.
// Otherwise taking the address of a value does not reproduce
// the named pointer type.
if v.Type().Name() == "" {
skipType = true // Let the underlying value print the type instead
}
out = opts.FormatValue(v.Elem(), t.Kind(), ptrs)
out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
out = &textWrap{Prefix: "&", Value: out}
return out
case reflect.Interface:
if v.IsNil() {
return textNil
}
// Interfaces accept different concrete types,
// so configure the underlying value to explicitly print the type.
return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs)
default:
panic(fmt.Sprintf("%v kind not handled", v.Kind()))
}
}
func (opts formatOptions) formatString(prefix, s string) textNode {
maxLen := len(s)
maxLines := strings.Count(s, "\n") + 1
if opts.LimitVerbosity {
maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc...
maxLines = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc...
}
// For multiline strings, use the triple-quote syntax,
// but only use it when printing removed or inserted nodes since
// we only want the extra verbosity for those cases.
lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n")
isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+')
for i := 0; i < len(lines) && isTripleQuoted; i++ {
lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
isPrintable := func(r rune) bool {
return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable
}
line := lines[i]
isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen
}
if isTripleQuoted {
var list textList
list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
for i, line := range lines {
if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 {
comment := commentString(fmt.Sprintf("%d elided lines", numElided))
list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment})
break
}
list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true})
}
list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
return &textWrap{Prefix: "(", Value: list, Suffix: ")"}
}
// Format the string as a single-line quoted string.
if len(s) > maxLen+len(textEllipsis) {
return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis))
}
return textLine(prefix + formatString(s))
}
// formatMapKey formats v as if it were a map key.
// The result is guaranteed to be a single line.
func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string {
var opts formatOptions
opts.DiffMode = diffIdentical
opts.TypeMode = elideType
opts.PrintAddresses = disambiguate
opts.AvoidStringer = disambiguate
opts.QualifiedNames = disambiguate
opts.VerbosityLevel = maxVerbosityPreset
opts.LimitVerbosity = true
s := opts.FormatValue(v, reflect.Map, ptrs).String()
return strings.TrimSpace(s)
}
// formatString prints s as a double-quoted or backtick-quoted string.
func formatString(s string) string {
// Use quoted string if it the same length as a raw string literal.
// Otherwise, attempt to use the raw string form.
qs := strconv.Quote(s)
if len(qs) == 1+len(s)+1 {
return qs
}
// Disallow newlines to ensure output is a single line.
// Only allow printable runes for readability purposes.
rawInvalid := func(r rune) bool {
return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
}
if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 {
return "`" + s + "`"
}
return qs
}
// formatHex prints u as a hexadecimal integer in Go notation.
func formatHex(u uint64) string {
var f string
switch {
case u <= 0xff:
f = "0x%02x"
case u <= 0xffff:
f = "0x%04x"
case u <= 0xffffff:
f = "0x%06x"
case u <= 0xffffffff:
f = "0x%08x"
case u <= 0xffffffffff:
f = "0x%010x"
case u <= 0xffffffffffff:
f = "0x%012x"
case u <= 0xffffffffffffff:
f = "0x%014x"
case u <= 0xffffffffffffffff:
f = "0x%016x"
}
return fmt.Sprintf(f, u)
}

614
vendor/github.com/google/go-cmp/cmp/report_slices.go generated vendored Normal file
View File

@@ -0,0 +1,614 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"bytes"
"fmt"
"math"
"reflect"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/google/go-cmp/cmp/internal/diff"
)
// CanFormatDiffSlice reports whether we support custom formatting for nodes
// that are slices of primitive kinds or strings.
func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
switch {
case opts.DiffMode != diffUnknown:
return false // Must be formatting in diff mode
case v.NumDiff == 0:
return false // No differences detected
case !v.ValueX.IsValid() || !v.ValueY.IsValid():
return false // Both values must be valid
case v.NumIgnored > 0:
return false // Some ignore option was used
case v.NumTransformed > 0:
return false // Some transform option was used
case v.NumCompared > 1:
return false // More than one comparison was used
case v.NumCompared == 1 && v.Type.Name() != "":
// The need for cmp to check applicability of options on every element
// in a slice is a significant performance detriment for large []byte.
// The workaround is to specify Comparer(bytes.Equal),
// which enables cmp to compare []byte more efficiently.
// If they differ, we still want to provide batched diffing.
// The logic disallows named types since they tend to have their own
// String method, with nicer formatting than what this provides.
return false
}
// Check whether this is an interface with the same concrete types.
t := v.Type
vx, vy := v.ValueX, v.ValueY
if t.Kind() == reflect.Interface && !vx.IsNil() && !vy.IsNil() && vx.Elem().Type() == vy.Elem().Type() {
vx, vy = vx.Elem(), vy.Elem()
t = vx.Type()
}
// Check whether we provide specialized diffing for this type.
switch t.Kind() {
case reflect.String:
case reflect.Array, reflect.Slice:
// Only slices of primitive types have specialized handling.
switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
default:
return false
}
// Both slice values have to be non-empty.
if t.Kind() == reflect.Slice && (vx.Len() == 0 || vy.Len() == 0) {
return false
}
// If a sufficient number of elements already differ,
// use specialized formatting even if length requirement is not met.
if v.NumDiff > v.NumSame {
return true
}
default:
return false
}
// Use specialized string diffing for longer slices or strings.
const minLength = 32
return vx.Len() >= minLength && vy.Len() >= minLength
}
// FormatDiffSlice prints a diff for the slices (or strings) represented by v.
// This provides custom-tailored logic to make printing of differences in
// textual strings and slices of primitive kinds more readable.
func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
assert(opts.DiffMode == diffUnknown)
t, vx, vy := v.Type, v.ValueX, v.ValueY
if t.Kind() == reflect.Interface {
vx, vy = vx.Elem(), vy.Elem()
t = vx.Type()
opts = opts.WithTypeMode(emitType)
}
// Auto-detect the type of the data.
var sx, sy string
var ssx, ssy []string
var isString, isMostlyText, isPureLinedText, isBinary bool
switch {
case t.Kind() == reflect.String:
sx, sy = vx.String(), vy.String()
isString = true
case t.Kind() == reflect.Slice && t.Elem() == byteType:
sx, sy = string(vx.Bytes()), string(vy.Bytes())
isString = true
case t.Kind() == reflect.Array:
// Arrays need to be addressable for slice operations to work.
vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem()
vx2.Set(vx)
vy2.Set(vy)
vx, vy = vx2, vy2
}
if isString {
var numTotalRunes, numValidRunes, numLines, lastLineIdx, maxLineLen int
for i, r := range sx + sy {
numTotalRunes++
if (unicode.IsPrint(r) || unicode.IsSpace(r)) && r != utf8.RuneError {
numValidRunes++
}
if r == '\n' {
if maxLineLen < i-lastLineIdx {
maxLineLen = i - lastLineIdx
}
lastLineIdx = i + 1
numLines++
}
}
isPureText := numValidRunes == numTotalRunes
isMostlyText = float64(numValidRunes) > math.Floor(0.90*float64(numTotalRunes))
isPureLinedText = isPureText && numLines >= 4 && maxLineLen <= 1024
isBinary = !isMostlyText
// Avoid diffing by lines if it produces a significantly more complex
// edit script than diffing by bytes.
if isPureLinedText {
ssx = strings.Split(sx, "\n")
ssy = strings.Split(sy, "\n")
esLines := diff.Difference(len(ssx), len(ssy), func(ix, iy int) diff.Result {
return diff.BoolResult(ssx[ix] == ssy[iy])
})
esBytes := diff.Difference(len(sx), len(sy), func(ix, iy int) diff.Result {
return diff.BoolResult(sx[ix] == sy[iy])
})
efficiencyLines := float64(esLines.Dist()) / float64(len(esLines))
efficiencyBytes := float64(esBytes.Dist()) / float64(len(esBytes))
quotedLength := len(strconv.Quote(sx + sy))
unquotedLength := len(sx) + len(sy)
escapeExpansionRatio := float64(quotedLength) / float64(unquotedLength)
isPureLinedText = efficiencyLines < 4*efficiencyBytes || escapeExpansionRatio > 1.1
}
}
// Format the string into printable records.
var list textList
var delim string
switch {
// If the text appears to be multi-lined text,
// then perform differencing across individual lines.
case isPureLinedText:
list = opts.formatDiffSlice(
reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line",
func(v reflect.Value, d diffMode) textRecord {
s := formatString(v.Index(0).String())
return textRecord{Diff: d, Value: textLine(s)}
},
)
delim = "\n"
// If possible, use a custom triple-quote (""") syntax for printing
// differences in a string literal. This format is more readable,
// but has edge-cases where differences are visually indistinguishable.
// This format is avoided under the following conditions:
// - A line starts with `"""`
// - A line starts with "..."
// - A line contains non-printable characters
// - Adjacent different lines differ only by whitespace
//
// For example:
//
// """
// ... // 3 identical lines
// foo
// bar
// - baz
// + BAZ
// """
isTripleQuoted := true
prevRemoveLines := map[string]bool{}
prevInsertLines := map[string]bool{}
var list2 textList
list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true})
for _, r := range list {
if !r.Value.Equal(textEllipsis) {
line, _ := strconv.Unquote(string(r.Value.(textLine)))
line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
normLine := strings.Map(func(r rune) rune {
if unicode.IsSpace(r) {
return -1 // drop whitespace to avoid visually indistinguishable output
}
return r
}, line)
isPrintable := func(r rune) bool {
return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable
}
isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == ""
switch r.Diff {
case diffRemoved:
isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine]
prevRemoveLines[normLine] = true
case diffInserted:
isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine]
prevInsertLines[normLine] = true
}
if !isTripleQuoted {
break
}
r.Value = textLine(line)
r.ElideComma = true
}
if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group
prevRemoveLines = map[string]bool{}
prevInsertLines = map[string]bool{}
}
list2 = append(list2, r)
}
if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 {
list2 = list2[:len(list2)-1] // elide single empty line at the end
}
list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true})
if isTripleQuoted {
var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"}
switch t.Kind() {
case reflect.String:
if t != stringType {
out = opts.FormatType(t, out)
}
case reflect.Slice:
// Always emit type for slices since the triple-quote syntax
// looks like a string (not a slice).
opts = opts.WithTypeMode(emitType)
out = opts.FormatType(t, out)
}
return out
}
// If the text appears to be single-lined text,
// then perform differencing in approximately fixed-sized chunks.
// The output is printed as quoted strings.
case isMostlyText:
list = opts.formatDiffSlice(
reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte",
func(v reflect.Value, d diffMode) textRecord {
s := formatString(v.String())
return textRecord{Diff: d, Value: textLine(s)}
},
)
// If the text appears to be binary data,
// then perform differencing in approximately fixed-sized chunks.
// The output is inspired by hexdump.
case isBinary:
list = opts.formatDiffSlice(
reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte",
func(v reflect.Value, d diffMode) textRecord {
var ss []string
for i := 0; i < v.Len(); i++ {
ss = append(ss, formatHex(v.Index(i).Uint()))
}
s := strings.Join(ss, ", ")
comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String())))
return textRecord{Diff: d, Value: textLine(s), Comment: comment}
},
)
// For all other slices of primitive types,
// then perform differencing in approximately fixed-sized chunks.
// The size of each chunk depends on the width of the element kind.
default:
var chunkSize int
if t.Elem().Kind() == reflect.Bool {
chunkSize = 16
} else {
switch t.Elem().Bits() {
case 8:
chunkSize = 16
case 16:
chunkSize = 12
case 32:
chunkSize = 8
default:
chunkSize = 8
}
}
list = opts.formatDiffSlice(
vx, vy, chunkSize, t.Elem().Kind().String(),
func(v reflect.Value, d diffMode) textRecord {
var ss []string
for i := 0; i < v.Len(); i++ {
switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
ss = append(ss, fmt.Sprint(v.Index(i).Int()))
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
ss = append(ss, fmt.Sprint(v.Index(i).Uint()))
case reflect.Uint8, reflect.Uintptr:
ss = append(ss, formatHex(v.Index(i).Uint()))
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
ss = append(ss, fmt.Sprint(v.Index(i).Interface()))
}
}
s := strings.Join(ss, ", ")
return textRecord{Diff: d, Value: textLine(s)}
},
)
}
// Wrap the output with appropriate type information.
var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
if !isMostlyText {
// The "{...}" byte-sequence literal is not valid Go syntax for strings.
// Emit the type for extra clarity (e.g. "string{...}").
if t.Kind() == reflect.String {
opts = opts.WithTypeMode(emitType)
}
return opts.FormatType(t, out)
}
switch t.Kind() {
case reflect.String:
out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)}
if t != stringType {
out = opts.FormatType(t, out)
}
case reflect.Slice:
out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)}
if t != bytesType {
out = opts.FormatType(t, out)
}
}
return out
}
// formatASCII formats s as an ASCII string.
// This is useful for printing binary strings in a semi-legible way.
func formatASCII(s string) string {
b := bytes.Repeat([]byte{'.'}, len(s))
for i := 0; i < len(s); i++ {
if ' ' <= s[i] && s[i] <= '~' {
b[i] = s[i]
}
}
return string(b)
}
func (opts formatOptions) formatDiffSlice(
vx, vy reflect.Value, chunkSize int, name string,
makeRec func(reflect.Value, diffMode) textRecord,
) (list textList) {
eq := func(ix, iy int) bool {
return vx.Index(ix).Interface() == vy.Index(iy).Interface()
}
es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result {
return diff.BoolResult(eq(ix, iy))
})
appendChunks := func(v reflect.Value, d diffMode) int {
n0 := v.Len()
for v.Len() > 0 {
n := chunkSize
if n > v.Len() {
n = v.Len()
}
list = append(list, makeRec(v.Slice(0, n), d))
v = v.Slice(n, v.Len())
}
return n0 - v.Len()
}
var numDiffs int
maxLen := -1
if opts.LimitVerbosity {
maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc...
opts.VerbosityLevel--
}
groups := coalesceAdjacentEdits(name, es)
groups = coalesceInterveningIdentical(groups, chunkSize/4)
groups = cleanupSurroundingIdentical(groups, eq)
maxGroup := diffStats{Name: name}
for i, ds := range groups {
if maxLen >= 0 && numDiffs >= maxLen {
maxGroup = maxGroup.Append(ds)
continue
}
// Print equal.
if ds.NumDiff() == 0 {
// Compute the number of leading and trailing equal bytes to print.
var numLo, numHi int
numEqual := ds.NumIgnored + ds.NumIdentical
for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 {
numLo++
}
for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 {
numHi++
}
if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 {
numHi = numEqual - numLo // Avoid pointless coalescing of single equal row
}
// Print the equal bytes.
appendChunks(vx.Slice(0, numLo), diffIdentical)
if numEqual > numLo+numHi {
ds.NumIdentical -= numLo + numHi
list.AppendEllipsis(ds)
}
appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical)
vx = vx.Slice(numEqual, vx.Len())
vy = vy.Slice(numEqual, vy.Len())
continue
}
// Print unequal.
len0 := len(list)
nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved)
vx = vx.Slice(nx, vx.Len())
ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted)
vy = vy.Slice(ny, vy.Len())
numDiffs += len(list) - len0
}
if maxGroup.IsZero() {
assert(vx.Len() == 0 && vy.Len() == 0)
} else {
list.AppendEllipsis(maxGroup)
}
return list
}
// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent
// equal or unequal counts.
//
// Example:
//
// Input: "..XXY...Y"
// Output: [
// {NumIdentical: 2},
// {NumRemoved: 2, NumInserted 1},
// {NumIdentical: 3},
// {NumInserted: 1},
// ]
func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) {
var prevMode byte
lastStats := func(mode byte) *diffStats {
if prevMode != mode {
groups = append(groups, diffStats{Name: name})
prevMode = mode
}
return &groups[len(groups)-1]
}
for _, e := range es {
switch e {
case diff.Identity:
lastStats('=').NumIdentical++
case diff.UniqueX:
lastStats('!').NumRemoved++
case diff.UniqueY:
lastStats('!').NumInserted++
case diff.Modified:
lastStats('!').NumModified++
}
}
return groups
}
// coalesceInterveningIdentical coalesces sufficiently short (<= windowSize)
// equal groups into adjacent unequal groups that currently result in a
// dual inserted/removed printout. This acts as a high-pass filter to smooth
// out high-frequency changes within the windowSize.
//
// Example:
//
// WindowSize: 16,
// Input: [
// {NumIdentical: 61}, // group 0
// {NumRemoved: 3, NumInserted: 1}, // group 1
// {NumIdentical: 6}, // ├── coalesce
// {NumInserted: 2}, // ├── coalesce
// {NumIdentical: 1}, // ├── coalesce
// {NumRemoved: 9}, // └── coalesce
// {NumIdentical: 64}, // group 2
// {NumRemoved: 3, NumInserted: 1}, // group 3
// {NumIdentical: 6}, // ├── coalesce
// {NumInserted: 2}, // ├── coalesce
// {NumIdentical: 1}, // ├── coalesce
// {NumRemoved: 7}, // ├── coalesce
// {NumIdentical: 1}, // ├── coalesce
// {NumRemoved: 2}, // └── coalesce
// {NumIdentical: 63}, // group 4
// ]
// Output: [
// {NumIdentical: 61},
// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3},
// {NumIdentical: 64},
// {NumIdentical: 8, NumRemoved: 12, NumInserted: 3},
// {NumIdentical: 63},
// ]
func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats {
groups, groupsOrig := groups[:0], groups
for i, ds := range groupsOrig {
if len(groups) >= 2 && ds.NumDiff() > 0 {
prev := &groups[len(groups)-2] // Unequal group
curr := &groups[len(groups)-1] // Equal group
next := &groupsOrig[i] // Unequal group
hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0
hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0
if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize {
*prev = prev.Append(*curr).Append(*next)
groups = groups[:len(groups)-1] // Truncate off equal group
continue
}
}
groups = append(groups, ds)
}
return groups
}
// cleanupSurroundingIdentical scans through all unequal groups, and
// moves any leading sequence of equal elements to the preceding equal group and
// moves and trailing sequence of equal elements to the succeeding equal group.
//
// This is necessary since coalesceInterveningIdentical may coalesce edit groups
// together such that leading/trailing spans of equal elements becomes possible.
// Note that this can occur even with an optimal diffing algorithm.
//
// Example:
//
// Input: [
// {NumIdentical: 61},
// {NumIdentical: 1 , NumRemoved: 11, NumInserted: 2}, // assume 3 leading identical elements
// {NumIdentical: 67},
// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, // assume 10 trailing identical elements
// {NumIdentical: 54},
// ]
// Output: [
// {NumIdentical: 64}, // incremented by 3
// {NumRemoved: 9},
// {NumIdentical: 67},
// {NumRemoved: 9},
// {NumIdentical: 64}, // incremented by 10
// ]
func cleanupSurroundingIdentical(groups []diffStats, eq func(i, j int) bool) []diffStats {
var ix, iy int // indexes into sequence x and y
for i, ds := range groups {
// Handle equal group.
if ds.NumDiff() == 0 {
ix += ds.NumIdentical
iy += ds.NumIdentical
continue
}
// Handle unequal group.
nx := ds.NumIdentical + ds.NumRemoved + ds.NumModified
ny := ds.NumIdentical + ds.NumInserted + ds.NumModified
var numLeadingIdentical, numTrailingIdentical int
for j := 0; j < nx && j < ny && eq(ix+j, iy+j); j++ {
numLeadingIdentical++
}
for j := 0; j < nx && j < ny && eq(ix+nx-1-j, iy+ny-1-j); j++ {
numTrailingIdentical++
}
if numIdentical := numLeadingIdentical + numTrailingIdentical; numIdentical > 0 {
if numLeadingIdentical > 0 {
// Remove leading identical span from this group and
// insert it into the preceding group.
if i-1 >= 0 {
groups[i-1].NumIdentical += numLeadingIdentical
} else {
// No preceding group exists, so prepend a new group,
// but do so after we finish iterating over all groups.
defer func() {
groups = append([]diffStats{{Name: groups[0].Name, NumIdentical: numLeadingIdentical}}, groups...)
}()
}
// Increment indexes since the preceding group would have handled this.
ix += numLeadingIdentical
iy += numLeadingIdentical
}
if numTrailingIdentical > 0 {
// Remove trailing identical span from this group and
// insert it into the succeeding group.
if i+1 < len(groups) {
groups[i+1].NumIdentical += numTrailingIdentical
} else {
// No succeeding group exists, so append a new group,
// but do so after we finish iterating over all groups.
defer func() {
groups = append(groups, diffStats{Name: groups[len(groups)-1].Name, NumIdentical: numTrailingIdentical})
}()
}
// Do not increment indexes since the succeeding group will handle this.
}
// Update this group since some identical elements were removed.
nx -= numIdentical
ny -= numIdentical
groups[i] = diffStats{Name: ds.Name, NumRemoved: nx, NumInserted: ny}
}
ix += nx
iy += ny
}
return groups
}

432
vendor/github.com/google/go-cmp/cmp/report_text.go generated vendored Normal file
View File

@@ -0,0 +1,432 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import (
"bytes"
"fmt"
"math/rand"
"strings"
"time"
"unicode/utf8"
"github.com/google/go-cmp/cmp/internal/flags"
)
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
const maxColumnLength = 80
type indentMode int
func (n indentMode) appendIndent(b []byte, d diffMode) []byte {
// The output of Diff is documented as being unstable to provide future
// flexibility in changing the output for more humanly readable reports.
// This logic intentionally introduces instability to the exact output
// so that users can detect accidental reliance on stability early on,
// rather than much later when an actual change to the format occurs.
if flags.Deterministic || randBool {
// Use regular spaces (U+0020).
switch d {
case diffUnknown, diffIdentical:
b = append(b, " "...)
case diffRemoved:
b = append(b, "- "...)
case diffInserted:
b = append(b, "+ "...)
}
} else {
// Use non-breaking spaces (U+00a0).
switch d {
case diffUnknown, diffIdentical:
b = append(b, "  "...)
case diffRemoved:
b = append(b, "- "...)
case diffInserted:
b = append(b, "+ "...)
}
}
return repeatCount(n).appendChar(b, '\t')
}
type repeatCount int
func (n repeatCount) appendChar(b []byte, c byte) []byte {
for ; n > 0; n-- {
b = append(b, c)
}
return b
}
// textNode is a simplified tree-based representation of structured text.
// Possible node types are textWrap, textList, or textLine.
type textNode interface {
// Len reports the length in bytes of a single-line version of the tree.
// Nested textRecord.Diff and textRecord.Comment fields are ignored.
Len() int
// Equal reports whether the two trees are structurally identical.
// Nested textRecord.Diff and textRecord.Comment fields are compared.
Equal(textNode) bool
// String returns the string representation of the text tree.
// It is not guaranteed that len(x.String()) == x.Len(),
// nor that x.String() == y.String() implies that x.Equal(y).
String() string
// formatCompactTo formats the contents of the tree as a single-line string
// to the provided buffer. Any nested textRecord.Diff and textRecord.Comment
// fields are ignored.
//
// However, not all nodes in the tree should be collapsed as a single-line.
// If a node can be collapsed as a single-line, it is replaced by a textLine
// node. Since the top-level node cannot replace itself, this also returns
// the current node itself.
//
// This does not mutate the receiver.
formatCompactTo([]byte, diffMode) ([]byte, textNode)
// formatExpandedTo formats the contents of the tree as a multi-line string
// to the provided buffer. In order for column alignment to operate well,
// formatCompactTo must be called before calling formatExpandedTo.
formatExpandedTo([]byte, diffMode, indentMode) []byte
}
// textWrap is a wrapper that concatenates a prefix and/or a suffix
// to the underlying node.
type textWrap struct {
Prefix string // e.g., "bytes.Buffer{"
Value textNode // textWrap | textList | textLine
Suffix string // e.g., "}"
Metadata interface{} // arbitrary metadata; has no effect on formatting
}
func (s *textWrap) Len() int {
return len(s.Prefix) + s.Value.Len() + len(s.Suffix)
}
func (s1 *textWrap) Equal(s2 textNode) bool {
if s2, ok := s2.(*textWrap); ok {
return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix
}
return false
}
func (s *textWrap) String() string {
var d diffMode
var n indentMode
_, s2 := s.formatCompactTo(nil, d)
b := n.appendIndent(nil, d) // Leading indent
b = s2.formatExpandedTo(b, d, n) // Main body
b = append(b, '\n') // Trailing newline
return string(b)
}
func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
n0 := len(b) // Original buffer length
b = append(b, s.Prefix...)
b, s.Value = s.Value.formatCompactTo(b, d)
b = append(b, s.Suffix...)
if _, ok := s.Value.(textLine); ok {
return b, textLine(b[n0:])
}
return b, s
}
func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
b = append(b, s.Prefix...)
b = s.Value.formatExpandedTo(b, d, n)
b = append(b, s.Suffix...)
return b
}
// textList is a comma-separated list of textWrap or textLine nodes.
// The list may be formatted as multi-lines or single-line at the discretion
// of the textList.formatCompactTo method.
type textList []textRecord
type textRecord struct {
Diff diffMode // e.g., 0 or '-' or '+'
Key string // e.g., "MyField"
Value textNode // textWrap | textLine
ElideComma bool // avoid trailing comma
Comment fmt.Stringer // e.g., "6 identical fields"
}
// AppendEllipsis appends a new ellipsis node to the list if none already
// exists at the end. If cs is non-zero it coalesces the statistics with the
// previous diffStats.
func (s *textList) AppendEllipsis(ds diffStats) {
hasStats := !ds.IsZero()
if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) {
if hasStats {
*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds})
} else {
*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true})
}
return
}
if hasStats {
(*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds)
}
}
func (s textList) Len() (n int) {
for i, r := range s {
n += len(r.Key)
if r.Key != "" {
n += len(": ")
}
n += r.Value.Len()
if i < len(s)-1 {
n += len(", ")
}
}
return n
}
func (s1 textList) Equal(s2 textNode) bool {
if s2, ok := s2.(textList); ok {
if len(s1) != len(s2) {
return false
}
for i := range s1 {
r1, r2 := s1[i], s2[i]
if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) {
return false
}
}
return true
}
return false
}
func (s textList) String() string {
return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String()
}
func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
s = append(textList(nil), s...) // Avoid mutating original
// Determine whether we can collapse this list as a single line.
n0 := len(b) // Original buffer length
var multiLine bool
for i, r := range s {
if r.Diff == diffInserted || r.Diff == diffRemoved {
multiLine = true
}
b = append(b, r.Key...)
if r.Key != "" {
b = append(b, ": "...)
}
b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff)
if _, ok := s[i].Value.(textLine); !ok {
multiLine = true
}
if r.Comment != nil {
multiLine = true
}
if i < len(s)-1 {
b = append(b, ", "...)
}
}
// Force multi-lined output when printing a removed/inserted node that
// is sufficiently long.
if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength {
multiLine = true
}
if !multiLine {
return b, textLine(b[n0:])
}
return b, s
}
func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
alignKeyLens := s.alignLens(
func(r textRecord) bool {
_, isLine := r.Value.(textLine)
return r.Key == "" || !isLine
},
func(r textRecord) int { return utf8.RuneCountInString(r.Key) },
)
alignValueLens := s.alignLens(
func(r textRecord) bool {
_, isLine := r.Value.(textLine)
return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil
},
func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) },
)
// Format lists of simple lists in a batched form.
// If the list is sequence of only textLine values,
// then batch multiple values on a single line.
var isSimple bool
for _, r := range s {
_, isLine := r.Value.(textLine)
isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil
if !isSimple {
break
}
}
if isSimple {
n++
var batch []byte
emitBatch := func() {
if len(batch) > 0 {
b = n.appendIndent(append(b, '\n'), d)
b = append(b, bytes.TrimRight(batch, " ")...)
batch = batch[:0]
}
}
for _, r := range s {
line := r.Value.(textLine)
if len(batch)+len(line)+len(", ") > maxColumnLength {
emitBatch()
}
batch = append(batch, line...)
batch = append(batch, ", "...)
}
emitBatch()
n--
return n.appendIndent(append(b, '\n'), d)
}
// Format the list as a multi-lined output.
n++
for i, r := range s {
b = n.appendIndent(append(b, '\n'), d|r.Diff)
if r.Key != "" {
b = append(b, r.Key+": "...)
}
b = alignKeyLens[i].appendChar(b, ' ')
b = r.Value.formatExpandedTo(b, d|r.Diff, n)
if !r.ElideComma {
b = append(b, ',')
}
b = alignValueLens[i].appendChar(b, ' ')
if r.Comment != nil {
b = append(b, " // "+r.Comment.String()...)
}
}
n--
return n.appendIndent(append(b, '\n'), d)
}
func (s textList) alignLens(
skipFunc func(textRecord) bool,
lenFunc func(textRecord) int,
) []repeatCount {
var startIdx, endIdx, maxLen int
lens := make([]repeatCount, len(s))
for i, r := range s {
if skipFunc(r) {
for j := startIdx; j < endIdx && j < len(s); j++ {
lens[j] = repeatCount(maxLen - lenFunc(s[j]))
}
startIdx, endIdx, maxLen = i+1, i+1, 0
} else {
if maxLen < lenFunc(r) {
maxLen = lenFunc(r)
}
endIdx = i + 1
}
}
for j := startIdx; j < endIdx && j < len(s); j++ {
lens[j] = repeatCount(maxLen - lenFunc(s[j]))
}
return lens
}
// textLine is a single-line segment of text and is always a leaf node
// in the textNode tree.
type textLine []byte
var (
textNil = textLine("nil")
textEllipsis = textLine("...")
)
func (s textLine) Len() int {
return len(s)
}
func (s1 textLine) Equal(s2 textNode) bool {
if s2, ok := s2.(textLine); ok {
return bytes.Equal([]byte(s1), []byte(s2))
}
return false
}
func (s textLine) String() string {
return string(s)
}
func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
return append(b, s...), s
}
func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte {
return append(b, s...)
}
type diffStats struct {
Name string
NumIgnored int
NumIdentical int
NumRemoved int
NumInserted int
NumModified int
}
func (s diffStats) IsZero() bool {
s.Name = ""
return s == diffStats{}
}
func (s diffStats) NumDiff() int {
return s.NumRemoved + s.NumInserted + s.NumModified
}
func (s diffStats) Append(ds diffStats) diffStats {
assert(s.Name == ds.Name)
s.NumIgnored += ds.NumIgnored
s.NumIdentical += ds.NumIdentical
s.NumRemoved += ds.NumRemoved
s.NumInserted += ds.NumInserted
s.NumModified += ds.NumModified
return s
}
// String prints a humanly-readable summary of coalesced records.
//
// Example:
//
// diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields"
func (s diffStats) String() string {
var ss []string
var sum int
labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"}
counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified}
for i, n := range counts {
if n > 0 {
ss = append(ss, fmt.Sprintf("%d %v", n, labels[i]))
}
sum += n
}
// Pluralize the name (adjusting for some obscure English grammar rules).
name := s.Name
if sum > 1 {
name += "s"
if strings.HasSuffix(name, "ys") {
name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries"
}
}
// Format the list according to English grammar (with Oxford comma).
switch n := len(ss); n {
case 0:
return ""
case 1, 2:
return strings.Join(ss, " and ") + " " + name
default:
return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name
}
}
type commentString string
func (s commentString) String() string { return string(s) }

121
vendor/github.com/google/go-cmp/cmp/report_value.go generated vendored Normal file
View File

@@ -0,0 +1,121 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmp
import "reflect"
// valueNode represents a single node within a report, which is a
// structured representation of the value tree, containing information
// regarding which nodes are equal or not.
type valueNode struct {
parent *valueNode
Type reflect.Type
ValueX reflect.Value
ValueY reflect.Value
// NumSame is the number of leaf nodes that are equal.
// All descendants are equal only if NumDiff is 0.
NumSame int
// NumDiff is the number of leaf nodes that are not equal.
NumDiff int
// NumIgnored is the number of leaf nodes that are ignored.
NumIgnored int
// NumCompared is the number of leaf nodes that were compared
// using an Equal method or Comparer function.
NumCompared int
// NumTransformed is the number of non-leaf nodes that were transformed.
NumTransformed int
// NumChildren is the number of transitive descendants of this node.
// This counts from zero; thus, leaf nodes have no descendants.
NumChildren int
// MaxDepth is the maximum depth of the tree. This counts from zero;
// thus, leaf nodes have a depth of zero.
MaxDepth int
// Records is a list of struct fields, slice elements, or map entries.
Records []reportRecord // If populated, implies Value is not populated
// Value is the result of a transformation, pointer indirect, of
// type assertion.
Value *valueNode // If populated, implies Records is not populated
// TransformerName is the name of the transformer.
TransformerName string // If non-empty, implies Value is populated
}
type reportRecord struct {
Key reflect.Value // Invalid for slice element
Value *valueNode
}
func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) {
vx, vy := ps.Values()
child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy}
switch s := ps.(type) {
case StructField:
assert(parent.Value == nil)
parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child})
case SliceIndex:
assert(parent.Value == nil)
parent.Records = append(parent.Records, reportRecord{Value: child})
case MapIndex:
assert(parent.Value == nil)
parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child})
case Indirect:
assert(parent.Value == nil && parent.Records == nil)
parent.Value = child
case TypeAssertion:
assert(parent.Value == nil && parent.Records == nil)
parent.Value = child
case Transform:
assert(parent.Value == nil && parent.Records == nil)
parent.Value = child
parent.TransformerName = s.Name()
parent.NumTransformed++
default:
assert(parent == nil) // Must be the root step
}
return child
}
func (r *valueNode) Report(rs Result) {
assert(r.MaxDepth == 0) // May only be called on leaf nodes
if rs.ByIgnore() {
r.NumIgnored++
} else {
if rs.Equal() {
r.NumSame++
} else {
r.NumDiff++
}
}
assert(r.NumSame+r.NumDiff+r.NumIgnored == 1)
if rs.ByMethod() {
r.NumCompared++
}
if rs.ByFunc() {
r.NumCompared++
}
assert(r.NumCompared <= 1)
}
func (child *valueNode) PopStep() (parent *valueNode) {
if child.parent == nil {
return nil
}
parent = child.parent
parent.NumSame += child.NumSame
parent.NumDiff += child.NumDiff
parent.NumIgnored += child.NumIgnored
parent.NumCompared += child.NumCompared
parent.NumTransformed += child.NumTransformed
parent.NumChildren += child.NumChildren + 1
if parent.MaxDepth < child.MaxDepth+1 {
parent.MaxDepth = child.MaxDepth + 1
}
return parent
}

23
vendor/github.com/google/nftables/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,23 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution,
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.

202
vendor/github.com/google/nftables/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
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
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

24
vendor/github.com/google/nftables/README.md generated vendored Normal file
View File

@@ -0,0 +1,24 @@
[![Build Status](https://github.com/google/nftables/actions/workflows/push.yml/badge.svg)](https://github.com/google/nftables/actions/workflows/push.yml)
[![GoDoc](https://godoc.org/github.com/google/nftables?status.svg)](https://godoc.org/github.com/google/nftables)
**This is not the correct repository for issues with the Linux nftables
project!** This repository contains a third-party Go package to programmatically
interact with nftables. Find the official nftables website at
https://wiki.nftables.org/
This package manipulates Linux nftables (the iptables successor). It is
implemented in pure Go, i.e. does not wrap libnftnl.
This is not an official Google product.
## Breaking changes
This package is in very early stages, and only contains enough data types and
functions to install very basic nftables rules. It is likely that mistakes with
the data types/API will be identified as more functionality is added.
## Contributions
Contributions are very welcome!

View File

@@ -0,0 +1,300 @@
// Package alignedbuff implements encoding and decoding aligned data elements
// to/from buffers in native endianess.
//
// # Note
//
// The alignment/padding as implemented in this package must match that of
// kernel's and user space C implementations for a particular architecture (bit
// size). Please see also the "dummy structure" _xt_align
// (https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/x_tables.h#L93)
// as well as the associated XT_ALIGN C preprocessor macro.
//
// In particular, we rely on the Go compiler to follow the same architecture
// alignments as the C compiler(s) on Linux.
package alignedbuff
import (
"bytes"
"errors"
"fmt"
"unsafe"
"github.com/google/nftables/binaryutil"
)
// ErrEOF signals trying to read beyond the available payload information.
var ErrEOF = errors.New("not enough data left")
// AlignedBuff implements marshalling and unmarshalling information in
// platform/architecture native endianess and data type alignment. It
// additionally covers some of the nftables-xtables translation-specific
// idiosyncracies to the extend needed in order to properly marshal and
// unmarshal Match and Target expressions, and their Info payload in particular.
type AlignedBuff struct {
data []byte
pos int
}
// New returns a new AlignedBuff for marshalling aligned data in native
// endianess.
func New() AlignedBuff {
return AlignedBuff{}
}
// NewWithData returns a new AlignedBuff for unmarshalling the passed data in
// native endianess.
func NewWithData(data []byte) AlignedBuff {
return AlignedBuff{data: data}
}
// Data returns the properly padded info payload data written before by calling
// the various Uint8, Uint16, ... marshalling functions.
func (a *AlignedBuff) Data() []byte {
// The Linux kernel expects payloads to be padded to the next uint64
// alignment.
a.alignWrite(uint64AlignMask)
return a.data
}
// BytesAligned32 unmarshals the given amount of bytes starting with the native
// alignment for uint32 data types. It returns ErrEOF when trying to read beyond
// the payload.
//
// BytesAligned32 is used to unmarshal IP addresses for different IP versions,
// which are always aligned the same way as the native alignment for uint32.
func (a *AlignedBuff) BytesAligned32(size int) ([]byte, error) {
if err := a.alignCheckedRead(uint32AlignMask); err != nil {
return nil, err
}
if a.pos > len(a.data)-size {
return nil, ErrEOF
}
data := a.data[a.pos : a.pos+size]
a.pos += size
return data, nil
}
// Uint8 unmarshals an uint8 in native endianess and alignment. It returns
// ErrEOF when trying to read beyond the payload.
func (a *AlignedBuff) Uint8() (uint8, error) {
if a.pos >= len(a.data) {
return 0, ErrEOF
}
v := a.data[a.pos]
a.pos++
return v, nil
}
// Uint16 unmarshals an uint16 in native endianess and alignment. It returns
// ErrEOF when trying to read beyond the payload.
func (a *AlignedBuff) Uint16() (uint16, error) {
if err := a.alignCheckedRead(uint16AlignMask); err != nil {
return 0, err
}
v := binaryutil.NativeEndian.Uint16(a.data[a.pos : a.pos+2])
a.pos += 2
return v, nil
}
// Uint16BE unmarshals an uint16 in "network" (=big endian) endianess and native
// uint16 alignment. It returns ErrEOF when trying to read beyond the payload.
func (a *AlignedBuff) Uint16BE() (uint16, error) {
if err := a.alignCheckedRead(uint16AlignMask); err != nil {
return 0, err
}
v := binaryutil.BigEndian.Uint16(a.data[a.pos : a.pos+2])
a.pos += 2
return v, nil
}
// Uint32 unmarshals an uint32 in native endianess and alignment. It returns
// ErrEOF when trying to read beyond the payload.
func (a *AlignedBuff) Uint32() (uint32, error) {
if err := a.alignCheckedRead(uint32AlignMask); err != nil {
return 0, err
}
v := binaryutil.NativeEndian.Uint32(a.data[a.pos : a.pos+4])
a.pos += 4
return v, nil
}
// Uint64 unmarshals an uint64 in native endianess and alignment. It returns
// ErrEOF when trying to read beyond the payload.
func (a *AlignedBuff) Uint64() (uint64, error) {
if err := a.alignCheckedRead(uint64AlignMask); err != nil {
return 0, err
}
v := binaryutil.NativeEndian.Uint64(a.data[a.pos : a.pos+8])
a.pos += 8
return v, nil
}
// Int32 unmarshals an int32 in native endianess and alignment. It returns
// ErrEOF when trying to read beyond the payload.
func (a *AlignedBuff) Int32() (int32, error) {
if err := a.alignCheckedRead(int32AlignMask); err != nil {
return 0, err
}
v := binaryutil.Int32(a.data[a.pos : a.pos+4])
a.pos += 4
return v, nil
}
// String unmarshals a null terminated string
func (a *AlignedBuff) String() (string, error) {
len := 0
for {
if a.data[a.pos+len] == 0x00 {
break
}
len++
}
v := binaryutil.String(a.data[a.pos : a.pos+len])
a.pos += len
return v, nil
}
// StringWithLength unmarshals a string of a given length (for non-null
// terminated strings)
func (a *AlignedBuff) StringWithLength(len int) (string, error) {
v := binaryutil.String(a.data[a.pos : a.pos+len])
a.pos += len
return v, nil
}
// Uint unmarshals an uint in native endianess and alignment for the C "unsigned
// int" type. It returns ErrEOF when trying to read beyond the payload. Please
// note that on 64bit platforms, the size and alignment of C's and Go's unsigned
// integer data types differ, so we encapsulate this difference here.
func (a *AlignedBuff) Uint() (uint, error) {
switch uintSize {
case 2:
v, err := a.Uint16()
return uint(v), err
case 4:
v, err := a.Uint32()
return uint(v), err
case 8:
v, err := a.Uint64()
return uint(v), err
default:
panic(fmt.Sprintf("unsupported uint size %d", uintSize))
}
}
// PutBytesAligned32 marshals the given bytes starting with the native alignment
// for uint32 data types. It additionaly adds padding to reach the specified
// size.
//
// PutBytesAligned32 is used to marshal IP addresses for different IP versions,
// which are always aligned the same way as the native alignment for uint32.
func (a *AlignedBuff) PutBytesAligned32(data []byte, size int) {
a.alignWrite(uint32AlignMask)
a.data = append(a.data, data...)
a.pos += len(data)
if len(data) < size {
padding := size - len(data)
a.data = append(a.data, bytes.Repeat([]byte{0}, padding)...)
a.pos += padding
}
}
// PutUint8 marshals an uint8 in native endianess and alignment.
func (a *AlignedBuff) PutUint8(v uint8) {
a.data = append(a.data, v)
a.pos++
}
// PutUint16 marshals an uint16 in native endianess and alignment.
func (a *AlignedBuff) PutUint16(v uint16) {
a.alignWrite(uint16AlignMask)
a.data = append(a.data, binaryutil.NativeEndian.PutUint16(v)...)
a.pos += 2
}
// PutUint16BE marshals an uint16 in "network" (=big endian) endianess and
// native uint16 alignment.
func (a *AlignedBuff) PutUint16BE(v uint16) {
a.alignWrite(uint16AlignMask)
a.data = append(a.data, binaryutil.BigEndian.PutUint16(v)...)
a.pos += 2
}
// PutUint32 marshals an uint32 in native endianess and alignment.
func (a *AlignedBuff) PutUint32(v uint32) {
a.alignWrite(uint32AlignMask)
a.data = append(a.data, binaryutil.NativeEndian.PutUint32(v)...)
a.pos += 4
}
// PutUint64 marshals an uint64 in native endianess and alignment.
func (a *AlignedBuff) PutUint64(v uint64) {
a.alignWrite(uint64AlignMask)
a.data = append(a.data, binaryutil.NativeEndian.PutUint64(v)...)
a.pos += 8
}
// PutInt32 marshals an int32 in native endianess and alignment.
func (a *AlignedBuff) PutInt32(v int32) {
a.alignWrite(int32AlignMask)
a.data = append(a.data, binaryutil.PutInt32(v)...)
a.pos += 4
}
// PutString marshals a string.
func (a *AlignedBuff) PutString(v string) {
a.data = append(a.data, binaryutil.PutString(v)...)
a.pos += len(v)
}
// PutUint marshals an uint in native endianess and alignment for the C
// "unsigned int" type. Please note that on 64bit platforms, the size and
// alignment of C's and Go's unsigned integer data types differ, so we
// encapsulate this difference here.
func (a *AlignedBuff) PutUint(v uint) {
switch uintSize {
case 2:
a.PutUint16(uint16(v))
case 4:
a.PutUint32(uint32(v))
case 8:
a.PutUint64(uint64(v))
default:
panic(fmt.Sprintf("unsupported uint size %d", uintSize))
}
}
// alignCheckedRead aligns the (read) position if necessary and suitable
// according to the specified alignment mask. alignCheckedRead returns an error
// if after any necessary alignment there isn't enough data left to be read into
// a value of the size corresponding to the specified alignment mask.
func (a *AlignedBuff) alignCheckedRead(m int) error {
a.pos = (a.pos + m) & ^m
if a.pos > len(a.data)-(m+1) {
return ErrEOF
}
return nil
}
// alignWrite aligns the (write) position if necessary and suitable according to
// the specified alignment mask. It doubles as final payload padding helpmate in
// order to keep the kernel happy.
func (a *AlignedBuff) alignWrite(m int) {
pos := (a.pos + m) & ^m
if pos != a.pos {
a.data = append(a.data, padding[:pos-a.pos]...)
a.pos = pos
}
}
// This is ... ugly.
var uint16AlignMask = int(unsafe.Alignof(uint16(0)) - 1)
var uint32AlignMask = int(unsafe.Alignof(uint32(0)) - 1)
var uint64AlignMask = int(unsafe.Alignof(uint64(0)) - 1)
var padding = bytes.Repeat([]byte{0}, uint64AlignMask)
var int32AlignMask = int(unsafe.Alignof(int32(0)) - 1)
// And this even worse.
var uintSize = unsafe.Sizeof(uint32(0))

View File

@@ -0,0 +1,125 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package binaryutil contains convenience wrappers around encoding/binary.
package binaryutil
import (
"bytes"
"encoding/binary"
"unsafe"
)
// ByteOrder is like binary.ByteOrder, but allocates memory and returns byte
// slices, for convenience.
type ByteOrder interface {
PutUint16(v uint16) []byte
PutUint32(v uint32) []byte
PutUint64(v uint64) []byte
Uint16(b []byte) uint16
Uint32(b []byte) uint32
Uint64(b []byte) uint64
}
// NativeEndian is either little endian or big endian, depending on the native
// endian-ness, and allocates memory and returns byte slices, for convenience.
var NativeEndian ByteOrder = &nativeEndian{}
type nativeEndian struct{}
func (nativeEndian) PutUint16(v uint16) []byte {
buf := make([]byte, 2)
*(*uint16)(unsafe.Pointer(&buf[0])) = v
return buf
}
func (nativeEndian) PutUint32(v uint32) []byte {
buf := make([]byte, 4)
*(*uint32)(unsafe.Pointer(&buf[0])) = v
return buf
}
func (nativeEndian) PutUint64(v uint64) []byte {
buf := make([]byte, 8)
*(*uint64)(unsafe.Pointer(&buf[0])) = v
return buf
}
func (nativeEndian) Uint16(b []byte) uint16 {
return *(*uint16)(unsafe.Pointer(&b[0]))
}
func (nativeEndian) Uint32(b []byte) uint32 {
return *(*uint32)(unsafe.Pointer(&b[0]))
}
func (nativeEndian) Uint64(b []byte) uint64 {
return *(*uint64)(unsafe.Pointer(&b[0]))
}
// BigEndian is like binary.BigEndian, but allocates memory and returns byte
// slices, for convenience.
var BigEndian ByteOrder = &bigEndian{}
type bigEndian struct{}
func (bigEndian) PutUint16(v uint16) []byte {
buf := make([]byte, 2)
binary.BigEndian.PutUint16(buf, v)
return buf
}
func (bigEndian) PutUint32(v uint32) []byte {
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, v)
return buf
}
func (bigEndian) PutUint64(v uint64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, v)
return buf
}
func (bigEndian) Uint16(b []byte) uint16 {
return binary.BigEndian.Uint16(b)
}
func (bigEndian) Uint32(b []byte) uint32 {
return binary.BigEndian.Uint32(b)
}
func (bigEndian) Uint64(b []byte) uint64 {
return binary.BigEndian.Uint64(b)
}
// For dealing with types not supported by the encoding/binary interface
func PutInt32(v int32) []byte {
buf := make([]byte, 4)
*(*int32)(unsafe.Pointer(&buf[0])) = v
return buf
}
func Int32(b []byte) int32 {
return *(*int32)(unsafe.Pointer(&b[0]))
}
func PutString(s string) []byte {
return []byte(s)
}
func String(b []byte) string {
return string(bytes.TrimRight(b, "\x00"))
}

328
vendor/github.com/google/nftables/chain.go generated vendored Normal file
View File

@@ -0,0 +1,328 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"encoding/binary"
"fmt"
"math"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// ChainHook specifies at which step in packet processing the Chain should be
// executed. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_hooks
type ChainHook uint32
// Possible ChainHook values.
var (
ChainHookPrerouting *ChainHook = ChainHookRef(unix.NF_INET_PRE_ROUTING)
ChainHookInput *ChainHook = ChainHookRef(unix.NF_INET_LOCAL_IN)
ChainHookForward *ChainHook = ChainHookRef(unix.NF_INET_FORWARD)
ChainHookOutput *ChainHook = ChainHookRef(unix.NF_INET_LOCAL_OUT)
ChainHookPostrouting *ChainHook = ChainHookRef(unix.NF_INET_POST_ROUTING)
ChainHookIngress *ChainHook = ChainHookRef(unix.NF_NETDEV_INGRESS)
ChainHookEgress *ChainHook = ChainHookRef(unix.NF_NETDEV_EGRESS)
)
// ChainHookRef returns a pointer to a ChainHookRef value.
func ChainHookRef(h ChainHook) *ChainHook {
return &h
}
// ChainPriority orders the chain relative to Netfilter internal operations. See
// also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_priority
type ChainPriority int32
// Possible ChainPriority values.
var ( // from /usr/include/linux/netfilter_ipv4.h
ChainPriorityFirst *ChainPriority = ChainPriorityRef(math.MinInt32)
ChainPriorityConntrackDefrag *ChainPriority = ChainPriorityRef(-400)
ChainPriorityRaw *ChainPriority = ChainPriorityRef(-300)
ChainPrioritySELinuxFirst *ChainPriority = ChainPriorityRef(-225)
ChainPriorityConntrack *ChainPriority = ChainPriorityRef(-200)
ChainPriorityMangle *ChainPriority = ChainPriorityRef(-150)
ChainPriorityNATDest *ChainPriority = ChainPriorityRef(-100)
ChainPriorityFilter *ChainPriority = ChainPriorityRef(0)
ChainPrioritySecurity *ChainPriority = ChainPriorityRef(50)
ChainPriorityNATSource *ChainPriority = ChainPriorityRef(100)
ChainPrioritySELinuxLast *ChainPriority = ChainPriorityRef(225)
ChainPriorityConntrackHelper *ChainPriority = ChainPriorityRef(300)
ChainPriorityConntrackConfirm *ChainPriority = ChainPriorityRef(math.MaxInt32)
ChainPriorityLast *ChainPriority = ChainPriorityRef(math.MaxInt32)
)
// ChainPriorityRef returns a pointer to a ChainPriority value.
func ChainPriorityRef(p ChainPriority) *ChainPriority {
return &p
}
// ChainType defines what this chain will be used for. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types
type ChainType string
// Possible ChainType values.
const (
ChainTypeFilter ChainType = "filter"
ChainTypeRoute ChainType = "route"
ChainTypeNAT ChainType = "nat"
)
// ChainPolicy defines what this chain default policy will be.
type ChainPolicy uint32
// Possible ChainPolicy values.
const (
ChainPolicyDrop ChainPolicy = iota
ChainPolicyAccept
)
// A Chain contains Rules. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains
type Chain struct {
Name string
Table *Table
Hooknum *ChainHook
Priority *ChainPriority
Type ChainType
Policy *ChainPolicy
Device string
}
// AddChain adds the specified Chain. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Adding_base_chains
func (cc *Conn) AddChain(c *Chain) *Chain {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_CHAIN_TABLE, Data: []byte(c.Table.Name + "\x00")},
{Type: unix.NFTA_CHAIN_NAME, Data: []byte(c.Name + "\x00")},
})
if c.Hooknum != nil && c.Priority != nil {
hookAttr := []netlink.Attribute{
{Type: unix.NFTA_HOOK_HOOKNUM, Data: binaryutil.BigEndian.PutUint32(uint32(*c.Hooknum))},
{Type: unix.NFTA_HOOK_PRIORITY, Data: binaryutil.BigEndian.PutUint32(uint32(*c.Priority))},
}
if c.Device != "" {
hookAttr = append(hookAttr, netlink.Attribute{Type: unix.NFTA_HOOK_DEV, Data: []byte(c.Device + "\x00")})
}
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NLA_F_NESTED | unix.NFTA_CHAIN_HOOK, Data: cc.marshalAttr(hookAttr)},
})...)
}
if c.Policy != nil {
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_CHAIN_POLICY, Data: binaryutil.BigEndian.PutUint32(uint32(*c.Policy))},
})...)
}
if c.Type != "" {
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_CHAIN_TYPE, Data: []byte(c.Type + "\x00")},
})...)
}
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWCHAIN),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: append(extraHeader(uint8(c.Table.Family), 0), data...),
})
return c
}
// DelChain deletes the specified Chain. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Deleting_chains
func (cc *Conn) DelChain(c *Chain) {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_CHAIN_TABLE, Data: []byte(c.Table.Name + "\x00")},
{Type: unix.NFTA_CHAIN_NAME, Data: []byte(c.Name + "\x00")},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELCHAIN),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(c.Table.Family), 0), data...),
})
}
// FlushChain removes all rules within the specified Chain. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Flushing_chain
func (cc *Conn) FlushChain(c *Chain) {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_TABLE, Data: []byte(c.Table.Name + "\x00")},
{Type: unix.NFTA_RULE_CHAIN, Data: []byte(c.Name + "\x00")},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELRULE),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(c.Table.Family), 0), data...),
})
}
// ListChains returns currently configured chains in the kernel
func (cc *Conn) ListChains() ([]*Chain, error) {
return cc.ListChainsOfTableFamily(TableFamilyUnspecified)
}
// ListChain returns a single chain configured in the specified table
func (cc *Conn) ListChain(table *Table, chain string) (*Chain, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
attrs := []netlink.Attribute{
{Type: unix.NFTA_TABLE_NAME, Data: []byte(table.Name + "\x00")},
{Type: unix.NFTA_CHAIN_NAME, Data: []byte(chain + "\x00")},
}
msg := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETCHAIN),
Flags: netlink.Request,
},
Data: append(extraHeader(uint8(table.Family), 0), cc.marshalAttr(attrs)...),
}
response, err := conn.Execute(msg)
if err != nil {
return nil, fmt.Errorf("conn.Execute failed: %v", err)
}
if got, want := len(response), 1; got != want {
return nil, fmt.Errorf("expected %d response message for chain, got %d", want, got)
}
ch, err := chainFromMsg(response[0])
if err != nil {
return nil, err
}
return ch, nil
}
// ListChainsOfTableFamily returns currently configured chains for the specified
// family in the kernel. It lists all chains ins all tables if family is
// TableFamilyUnspecified.
func (cc *Conn) ListChainsOfTableFamily(family TableFamily) ([]*Chain, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
msg := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETCHAIN),
Flags: netlink.Request | netlink.Dump,
},
Data: extraHeader(uint8(family), 0),
}
response, err := conn.Execute(msg)
if err != nil {
return nil, err
}
var chains []*Chain
for _, m := range response {
c, err := chainFromMsg(m)
if err != nil {
return nil, err
}
chains = append(chains, c)
}
return chains, nil
}
func chainFromMsg(msg netlink.Message) (*Chain, error) {
newChainHeaderType := netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWCHAIN)
delChainHeaderType := netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELCHAIN)
if got, want1, want2 := msg.Header.Type, newChainHeaderType, delChainHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
var c Chain
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
return nil, err
}
for ad.Next() {
switch ad.Type() {
case unix.NFTA_CHAIN_NAME:
c.Name = ad.String()
case unix.NFTA_TABLE_NAME:
c.Table = &Table{Name: ad.String()}
// msg[0] carries TableFamily byte indicating whether it is IPv4, IPv6 or something else
c.Table.Family = TableFamily(msg.Data[0])
case unix.NFTA_CHAIN_TYPE:
c.Type = ChainType(ad.String())
case unix.NFTA_CHAIN_POLICY:
policy := ChainPolicy(binaryutil.BigEndian.Uint32(ad.Bytes()))
c.Policy = &policy
case unix.NFTA_CHAIN_HOOK:
ad.Do(func(b []byte) error {
c.Hooknum, c.Priority, err = hookFromMsg(b)
return err
})
}
}
return &c, nil
}
func hookFromMsg(b []byte) (*ChainHook, *ChainPriority, error) {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return nil, nil, err
}
ad.ByteOrder = binary.BigEndian
var hooknum ChainHook
var prio ChainPriority
for ad.Next() {
switch ad.Type() {
case unix.NFTA_HOOK_HOOKNUM:
hooknum = ChainHook(ad.Uint32())
case unix.NFTA_HOOK_PRIORITY:
prio = ChainPriority(ad.Uint32())
}
}
return &hooknum, &prio, nil
}

89
vendor/github.com/google/nftables/compat_policy.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
package nftables
import (
"fmt"
"github.com/google/nftables/expr"
"golang.org/x/sys/unix"
)
const nft_RULE_COMPAT_F_INV uint32 = (1 << 1)
const nft_RULE_COMPAT_F_MASK uint32 = nft_RULE_COMPAT_F_INV
// Used by xt match or target like xt_tcpudp to set compat policy between xtables and nftables
// https://elixir.bootlin.com/linux/v5.12/source/net/netfilter/nft_compat.c#L187
type compatPolicy struct {
Proto uint32
Flag uint32
}
var xtMatchCompatMap map[string]*compatPolicy = map[string]*compatPolicy{
"tcp": {
Proto: unix.IPPROTO_TCP,
},
"udp": {
Proto: unix.IPPROTO_UDP,
},
"udplite": {
Proto: unix.IPPROTO_UDPLITE,
},
"tcpmss": {
Proto: unix.IPPROTO_TCP,
},
"sctp": {
Proto: unix.IPPROTO_SCTP,
},
"osf": {
Proto: unix.IPPROTO_TCP,
},
"ipcomp": {
Proto: unix.IPPROTO_COMP,
},
"esp": {
Proto: unix.IPPROTO_ESP,
},
}
var xtTargetCompatMap map[string]*compatPolicy = map[string]*compatPolicy{
"TCPOPTSTRIP": {
Proto: unix.IPPROTO_TCP,
},
"TCPMSS": {
Proto: unix.IPPROTO_TCP,
},
}
func getCompatPolicy(exprs []expr.Any) (*compatPolicy, error) {
var exprItem expr.Any
var compat *compatPolicy
for _, iter := range exprs {
var tmpExprItem expr.Any
var tmpCompat *compatPolicy
switch item := iter.(type) {
case *expr.Match:
if compat, ok := xtMatchCompatMap[item.Name]; ok {
tmpCompat = compat
tmpExprItem = item
} else {
continue
}
case *expr.Target:
if compat, ok := xtTargetCompatMap[item.Name]; ok {
tmpCompat = compat
tmpExprItem = item
} else {
continue
}
default:
continue
}
if compat == nil {
compat = tmpCompat
exprItem = tmpExprItem
} else if *compat != *tmpCompat {
return nil, fmt.Errorf("%#v and %#v has conflict compat policy %#v vs %#v", exprItem, tmpExprItem, compat, tmpCompat)
}
}
return compat, nil
}

341
vendor/github.com/google/nftables/conn.go generated vendored Normal file
View File

@@ -0,0 +1,341 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"errors"
"fmt"
"os"
"sync"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/expr"
"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nltest"
"golang.org/x/sys/unix"
)
// A Conn represents a netlink connection of the nftables family.
//
// All methods return their input, so that variables can be defined from string
// literals when desired.
//
// Commands are buffered. Flush sends all buffered commands in a single batch.
type Conn struct {
TestDial nltest.Func // for testing only; passed to nltest.Dial
NetNS int // fd referencing the network namespace netlink will interact with.
lasting bool // establish a lasting connection to be used across multiple netlink operations.
mu sync.Mutex // protects the following state
messages []netlink.Message
err error
nlconn *netlink.Conn // netlink socket using NETLINK_NETFILTER protocol.
}
// ConnOption is an option to change the behavior of the nftables Conn returned by Open.
type ConnOption func(*Conn)
// New returns a netlink connection for querying and modifying nftables. Some
// aspects of the new netlink connection can be configured using the options
// WithNetNSFd, WithTestDial, and AsLasting.
//
// A lasting netlink connection should be closed by calling CloseLasting() to
// close the underlying lasting netlink connection, cancelling all pending
// operations using this connection.
func New(opts ...ConnOption) (*Conn, error) {
cc := &Conn{}
for _, opt := range opts {
opt(cc)
}
if !cc.lasting {
return cc, nil
}
nlconn, err := cc.dialNetlink()
if err != nil {
return nil, err
}
cc.nlconn = nlconn
return cc, nil
}
// AsLasting creates the new netlink connection as a lasting connection that is
// reused across multiple netlink operations, instead of opening and closing the
// underlying netlink connection only for the duration of a single netlink
// operation.
func AsLasting() ConnOption {
return func(cc *Conn) {
// We cannot create the underlying connection yet, as we are called
// anywhere in the option processing chain and there might be later
// options still modifying connection behavior.
cc.lasting = true
}
}
// WithNetNSFd sets the network namespace to create a new netlink connection to:
// the fd must reference a network namespace.
func WithNetNSFd(fd int) ConnOption {
return func(cc *Conn) {
cc.NetNS = fd
}
}
// WithTestDial sets the specified nltest.Func when creating a new netlink
// connection.
func WithTestDial(f nltest.Func) ConnOption {
return func(cc *Conn) {
cc.TestDial = f
}
}
// netlinkCloser is returned by netlinkConn(UnderLock) and must be called after
// being done with the returned netlink connection in order to properly close
// this connection, if necessary.
type netlinkCloser func() error
// netlinkConn returns a netlink connection together with a netlinkCloser that
// later must be called by the caller when it doesn't need the returned netlink
// connection anymore. The netlinkCloser will close the netlink connection when
// necessary. If New has been told to create a lasting connection, then this
// lasting netlink connection will be returned, otherwise a new "transient"
// netlink connection will be opened and returned instead. netlinkConn must not
// be called while the Conn.mu lock is currently helt (this will cause a
// deadlock). Use netlinkConnUnderLock instead in such situations.
func (cc *Conn) netlinkConn() (*netlink.Conn, netlinkCloser, error) {
cc.mu.Lock()
defer cc.mu.Unlock()
return cc.netlinkConnUnderLock()
}
// netlinkConnUnderLock works like netlinkConn but must be called while holding
// the Conn.mu lock.
func (cc *Conn) netlinkConnUnderLock() (*netlink.Conn, netlinkCloser, error) {
if cc.nlconn != nil {
return cc.nlconn, func() error { return nil }, nil
}
nlconn, err := cc.dialNetlink()
if err != nil {
return nil, nil, err
}
return nlconn, func() error { return nlconn.Close() }, nil
}
func receiveAckAware(nlconn *netlink.Conn, sentMsgFlags netlink.HeaderFlags) ([]netlink.Message, error) {
if nlconn == nil {
return nil, errors.New("netlink conn is not initialized")
}
// first receive will be the message that we expect
reply, err := nlconn.Receive()
if err != nil {
return nil, err
}
if (sentMsgFlags & netlink.Acknowledge) == 0 {
// we did not request an ack
return reply, nil
}
if (sentMsgFlags & netlink.Dump) == netlink.Dump {
// sent message has Dump flag set, there will be no acks
// https://github.com/torvalds/linux/blob/7e062cda7d90543ac8c7700fc7c5527d0c0f22ad/net/netlink/af_netlink.c#L2387-L2390
return reply, nil
}
if len(reply) != 0 {
last := reply[len(reply)-1]
for re := last.Header.Type; (re&netlink.Overrun) == netlink.Overrun && (re&netlink.Done) != netlink.Done; re = last.Header.Type {
// we are not finished, the message is overrun
r, err := nlconn.Receive()
if err != nil {
return nil, err
}
reply = append(reply, r...)
last = reply[len(reply)-1]
}
if last.Header.Type == netlink.Error && binaryutil.BigEndian.Uint32(last.Data[:4]) == 0 {
// we have already collected an ack
return reply, nil
}
}
// Now we expect an ack
ack, err := nlconn.Receive()
if err != nil {
return nil, err
}
if len(ack) == 0 {
// received an empty ack?
return reply, nil
}
msg := ack[0]
if msg.Header.Type != netlink.Error {
// acks should be delivered as NLMSG_ERROR
return nil, fmt.Errorf("expected header %v, but got %v", netlink.Error, msg.Header.Type)
}
if binaryutil.BigEndian.Uint32(msg.Data[:4]) != 0 {
// if errno field is not set to 0 (success), this is an error
return nil, fmt.Errorf("error delivered in message: %v", msg.Data)
}
return reply, nil
}
// CloseLasting closes the lasting netlink connection that has been opened using
// AsLasting option when creating this connection. If either no lasting netlink
// connection has been opened or the lasting connection is already in the
// process of closing or has been closed, CloseLasting will immediately return
// without any error.
//
// CloseLasting will terminate all pending netlink operations using the lasting
// connection.
//
// After closing a lasting connection, the connection will revert to using
// on-demand transient netlink connections when calling further netlink
// operations (such as GetTables).
func (cc *Conn) CloseLasting() error {
// Don't acquire the lock for the whole duration of the CloseLasting
// operation, but instead only so long as to make sure to only run the
// netlink socket close on the first time with a lasting netlink socket. As
// there is only the New() constructor, but no Open() method, it's
// impossible to reopen a lasting connection.
cc.mu.Lock()
nlconn := cc.nlconn
cc.nlconn = nil
cc.mu.Unlock()
if nlconn != nil {
return nlconn.Close()
}
return nil
}
// Flush sends all buffered commands in a single batch to nftables.
func (cc *Conn) Flush() error {
cc.mu.Lock()
defer func() {
cc.messages = nil
cc.mu.Unlock()
}()
if len(cc.messages) == 0 {
// Messages were already programmed, returning nil
return nil
}
if cc.err != nil {
return cc.err // serialization error
}
conn, closer, err := cc.netlinkConnUnderLock()
if err != nil {
return err
}
defer func() { _ = closer() }()
if _, err := conn.SendMessages(batch(cc.messages)); err != nil {
return fmt.Errorf("SendMessages: %w", err)
}
var errs error
// Fetch the requested acknowledgement for each message we sent.
for _, msg := range cc.messages {
if _, err := receiveAckAware(conn, msg.Header.Flags); err != nil {
if errors.Is(err, os.ErrPermission) {
// Kernel will only send one permission error to user space.
return err
}
errs = errors.Join(errs, err)
}
}
if errs != nil {
return fmt.Errorf("conn.Receive: %w", errs)
}
return nil
}
// FlushRuleset flushes the entire ruleset. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Operations_at_ruleset_level
func (cc *Conn) FlushRuleset() {
cc.mu.Lock()
defer cc.mu.Unlock()
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELTABLE),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: extraHeader(0, 0),
})
}
func (cc *Conn) dialNetlink() (*netlink.Conn, error) {
if cc.TestDial != nil {
return nltest.Dial(cc.TestDial), nil
}
return netlink.Dial(unix.NETLINK_NETFILTER, &netlink.Config{NetNS: cc.NetNS})
}
func (cc *Conn) setErr(err error) {
if cc.err != nil {
return
}
cc.err = err
}
func (cc *Conn) marshalAttr(attrs []netlink.Attribute) []byte {
b, err := netlink.MarshalAttributes(attrs)
if err != nil {
cc.setErr(err)
return nil
}
return b
}
func (cc *Conn) marshalExpr(fam byte, e expr.Any) []byte {
b, err := expr.Marshal(fam, e)
if err != nil {
cc.setErr(err)
return nil
}
return b
}
func batch(messages []netlink.Message) []netlink.Message {
batch := []netlink.Message{
{
Header: netlink.Header{
Type: netlink.HeaderType(unix.NFNL_MSG_BATCH_BEGIN),
Flags: netlink.Request,
},
Data: extraHeader(0, unix.NFNL_SUBSYS_NFTABLES),
},
}
batch = append(batch, messages...)
batch = append(batch, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType(unix.NFNL_MSG_BATCH_END),
Flags: netlink.Request,
},
Data: extraHeader(0, unix.NFNL_SUBSYS_NFTABLES),
})
return batch
}

69
vendor/github.com/google/nftables/counter.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// CounterObj implements Obj.
type CounterObj struct {
Table *Table
Name string // e.g. “fwded”
Bytes uint64
Packets uint64
}
func (c *CounterObj) unmarshal(ad *netlink.AttributeDecoder) error {
for ad.Next() {
switch ad.Type() {
case unix.NFTA_COUNTER_BYTES:
c.Bytes = ad.Uint64()
case unix.NFTA_COUNTER_PACKETS:
c.Packets = ad.Uint64()
}
}
return ad.Err()
}
func (c *CounterObj) table() *Table {
return c.Table
}
func (c *CounterObj) family() TableFamily {
return c.Table.Family
}
func (c *CounterObj) marshal(data bool) ([]byte, error) {
obj, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_COUNTER_BYTES, Data: binaryutil.BigEndian.PutUint64(c.Bytes)},
{Type: unix.NFTA_COUNTER_PACKETS, Data: binaryutil.BigEndian.PutUint64(c.Packets)},
})
if err != nil {
return nil, err
}
attrs := []netlink.Attribute{
{Type: unix.NFTA_OBJ_TABLE, Data: []byte(c.Table.Name + "\x00")},
{Type: unix.NFTA_OBJ_NAME, Data: []byte(c.Name + "\x00")},
{Type: unix.NFTA_OBJ_TYPE, Data: binaryutil.BigEndian.PutUint32(unix.NFT_OBJECT_COUNTER)},
}
if data {
attrs = append(attrs, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_OBJ_DATA, Data: obj})
}
return netlink.MarshalAttributes(attrs)
}

16
vendor/github.com/google/nftables/doc.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package nftables manipulates Linux nftables (the iptables successor).
package nftables

102
vendor/github.com/google/nftables/expr/bitwise.go generated vendored Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Bitwise struct {
SourceRegister uint32
DestRegister uint32
Len uint32
Mask []byte
Xor []byte
}
func (e *Bitwise) marshal(fam byte) ([]byte, error) {
mask, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: e.Mask},
})
if err != nil {
return nil, err
}
xor, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: e.Xor},
})
if err != nil {
return nil, err
}
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_BITWISE_SREG, Data: binaryutil.BigEndian.PutUint32(e.SourceRegister)},
{Type: unix.NFTA_BITWISE_DREG, Data: binaryutil.BigEndian.PutUint32(e.DestRegister)},
{Type: unix.NFTA_BITWISE_LEN, Data: binaryutil.BigEndian.PutUint32(e.Len)},
{Type: unix.NLA_F_NESTED | unix.NFTA_BITWISE_MASK, Data: mask},
{Type: unix.NLA_F_NESTED | unix.NFTA_BITWISE_XOR, Data: xor},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("bitwise\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Bitwise) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_BITWISE_SREG:
e.SourceRegister = ad.Uint32()
case unix.NFTA_BITWISE_DREG:
e.DestRegister = ad.Uint32()
case unix.NFTA_BITWISE_LEN:
e.Len = ad.Uint32()
case unix.NFTA_BITWISE_MASK:
// Since NFTA_BITWISE_MASK is nested, it requires additional decoding
ad.Nested(func(nad *netlink.AttributeDecoder) error {
for nad.Next() {
switch nad.Type() {
case unix.NFTA_DATA_VALUE:
e.Mask = nad.Bytes()
}
}
return nil
})
case unix.NFTA_BITWISE_XOR:
// Since NFTA_BITWISE_XOR is nested, it requires additional decoding
ad.Nested(func(nad *netlink.AttributeDecoder) error {
for nad.Next() {
switch nad.Type() {
case unix.NFTA_DATA_VALUE:
e.Xor = nad.Bytes()
}
}
return nil
})
}
}
return ad.Err()
}

59
vendor/github.com/google/nftables/expr/byteorder.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type ByteorderOp uint32
const (
ByteorderNtoh ByteorderOp = unix.NFT_BYTEORDER_NTOH
ByteorderHton ByteorderOp = unix.NFT_BYTEORDER_HTON
)
type Byteorder struct {
SourceRegister uint32
DestRegister uint32
Op ByteorderOp
Len uint32
Size uint32
}
func (e *Byteorder) marshal(fam byte) ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_BYTEORDER_SREG, Data: binaryutil.BigEndian.PutUint32(e.SourceRegister)},
{Type: unix.NFTA_BYTEORDER_DREG, Data: binaryutil.BigEndian.PutUint32(e.DestRegister)},
{Type: unix.NFTA_BYTEORDER_OP, Data: binaryutil.BigEndian.PutUint32(uint32(e.Op))},
{Type: unix.NFTA_BYTEORDER_LEN, Data: binaryutil.BigEndian.PutUint32(e.Len)},
{Type: unix.NFTA_BYTEORDER_SIZE, Data: binaryutil.BigEndian.PutUint32(e.Size)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("byteorder\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Byteorder) unmarshal(fam byte, data []byte) error {
return fmt.Errorf("not yet implemented")
}

70
vendor/github.com/google/nftables/expr/connlimit.go generated vendored Normal file
View File

@@ -0,0 +1,70 @@
// Copyright 2019 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
const (
// Per https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n1167
NFTA_CONNLIMIT_UNSPEC = iota
NFTA_CONNLIMIT_COUNT
NFTA_CONNLIMIT_FLAGS
NFT_CONNLIMIT_F_INV = 1
)
// Per https://git.netfilter.org/libnftnl/tree/src/expr/connlimit.c?id=84d12cfacf8ddd857a09435f3d982ab6250d250c
type Connlimit struct {
Count uint32
Flags uint32
}
func (e *Connlimit) marshal(fam byte) ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: NFTA_CONNLIMIT_COUNT, Data: binaryutil.BigEndian.PutUint32(e.Count)},
{Type: NFTA_CONNLIMIT_FLAGS, Data: binaryutil.BigEndian.PutUint32(e.Flags)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("connlimit\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Connlimit) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case NFTA_CONNLIMIT_COUNT:
e.Count = binaryutil.BigEndian.Uint32(ad.Bytes())
case NFTA_CONNLIMIT_FLAGS:
e.Flags = binaryutil.BigEndian.Uint32(ad.Bytes())
}
}
return ad.Err()
}

60
vendor/github.com/google/nftables/expr/counter.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Counter struct {
Bytes uint64
Packets uint64
}
func (e *Counter) marshal(fam byte) ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_COUNTER_BYTES, Data: binaryutil.BigEndian.PutUint64(e.Bytes)},
{Type: unix.NFTA_COUNTER_PACKETS, Data: binaryutil.BigEndian.PutUint64(e.Packets)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("counter\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Counter) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_COUNTER_BYTES:
e.Bytes = ad.Uint64()
case unix.NFTA_COUNTER_PACKETS:
e.Packets = ad.Uint64()
}
}
return ad.Err()
}

115
vendor/github.com/google/nftables/expr/ct.go generated vendored Normal file
View File

@@ -0,0 +1,115 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// CtKey specifies which piece of conntrack information should be loaded. See
// also https://wiki.nftables.org/wiki-nftables/index.php/Matching_connection_tracking_stateful_metainformation
type CtKey uint32
// Possible CtKey values.
const (
CtKeySTATE CtKey = unix.NFT_CT_STATE
CtKeyDIRECTION CtKey = unix.NFT_CT_DIRECTION
CtKeySTATUS CtKey = unix.NFT_CT_STATUS
CtKeyMARK CtKey = unix.NFT_CT_MARK
CtKeySECMARK CtKey = unix.NFT_CT_SECMARK
CtKeyEXPIRATION CtKey = unix.NFT_CT_EXPIRATION
CtKeyHELPER CtKey = unix.NFT_CT_HELPER
CtKeyL3PROTOCOL CtKey = unix.NFT_CT_L3PROTOCOL
CtKeySRC CtKey = unix.NFT_CT_SRC
CtKeyDST CtKey = unix.NFT_CT_DST
CtKeyPROTOCOL CtKey = unix.NFT_CT_PROTOCOL
CtKeyPROTOSRC CtKey = unix.NFT_CT_PROTO_SRC
CtKeyPROTODST CtKey = unix.NFT_CT_PROTO_DST
CtKeyLABELS CtKey = unix.NFT_CT_LABELS
CtKeyPKTS CtKey = unix.NFT_CT_PKTS
CtKeyBYTES CtKey = unix.NFT_CT_BYTES
CtKeyAVGPKT CtKey = unix.NFT_CT_AVGPKT
CtKeyZONE CtKey = unix.NFT_CT_ZONE
CtKeyEVENTMASK CtKey = unix.NFT_CT_EVENTMASK
// https://sources.debian.org/src//nftables/0.9.8-3/src/ct.c/?hl=39#L39
CtStateBitINVALID uint32 = 1
CtStateBitESTABLISHED uint32 = 2
CtStateBitRELATED uint32 = 4
CtStateBitNEW uint32 = 8
CtStateBitUNTRACKED uint32 = 64
)
// Ct defines type for NFT connection tracking
type Ct struct {
Register uint32
SourceRegister bool
Key CtKey
}
func (e *Ct) marshal(fam byte) ([]byte, error) {
regData := []byte{}
exprData, err := netlink.MarshalAttributes(
[]netlink.Attribute{
{Type: unix.NFTA_CT_KEY, Data: binaryutil.BigEndian.PutUint32(uint32(e.Key))},
},
)
if err != nil {
return nil, err
}
if e.SourceRegister {
regData, err = netlink.MarshalAttributes(
[]netlink.Attribute{
{Type: unix.NFTA_CT_SREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
},
)
} else {
regData, err = netlink.MarshalAttributes(
[]netlink.Attribute{
{Type: unix.NFTA_CT_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
},
)
}
if err != nil {
return nil, err
}
exprData = append(exprData, regData...)
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("ct\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: exprData},
})
}
func (e *Ct) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_CT_KEY:
e.Key = CtKey(ad.Uint32())
case unix.NFTA_CT_DREG:
e.Register = ad.Uint32()
}
}
return ad.Err()
}

67
vendor/github.com/google/nftables/expr/dup.go generated vendored Normal file
View File

@@ -0,0 +1,67 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Dup struct {
RegAddr uint32
RegDev uint32
IsRegDevSet bool
}
func (e *Dup) marshal(fam byte) ([]byte, error) {
attrs := []netlink.Attribute{
{Type: unix.NFTA_DUP_SREG_ADDR, Data: binaryutil.BigEndian.PutUint32(e.RegAddr)},
}
if e.IsRegDevSet {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_DUP_SREG_DEV, Data: binaryutil.BigEndian.PutUint32(e.RegDev)})
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("dup\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Dup) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_DUP_SREG_ADDR:
e.RegAddr = ad.Uint32()
case unix.NFTA_DUP_SREG_DEV:
e.RegDev = ad.Uint32()
}
}
return ad.Err()
}

149
vendor/github.com/google/nftables/expr/dynset.go generated vendored Normal file
View File

@@ -0,0 +1,149 @@
// Copyright 2020 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"time"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/internal/parseexprfunc"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// Not yet supported by unix package
// https://cs.opensource.google/go/x/sys/+/c6bc011c:unix/ztypes_linux.go;l=2027-2036
const (
NFTA_DYNSET_EXPRESSIONS = 0xa
NFT_DYNSET_F_EXPR = (1 << 1)
)
// Dynset represent a rule dynamically adding or updating a set or a map based on an incoming packet.
type Dynset struct {
SrcRegKey uint32
SrcRegData uint32
SetID uint32
SetName string
Operation uint32
Timeout time.Duration
Invert bool
Exprs []Any
}
func (e *Dynset) marshal(fam byte) ([]byte, error) {
// See: https://git.netfilter.org/libnftnl/tree/src/expr/dynset.c
var opAttrs []netlink.Attribute
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_DYNSET_SREG_KEY, Data: binaryutil.BigEndian.PutUint32(e.SrcRegKey)})
if e.SrcRegData != 0 {
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_DYNSET_SREG_DATA, Data: binaryutil.BigEndian.PutUint32(e.SrcRegData)})
}
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_DYNSET_OP, Data: binaryutil.BigEndian.PutUint32(e.Operation)})
if e.Timeout != 0 {
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_DYNSET_TIMEOUT, Data: binaryutil.BigEndian.PutUint64(uint64(e.Timeout.Milliseconds()))})
}
var flags uint32
if e.Invert {
flags |= unix.NFT_DYNSET_F_INV
}
opAttrs = append(opAttrs,
netlink.Attribute{Type: unix.NFTA_DYNSET_SET_NAME, Data: []byte(e.SetName + "\x00")},
netlink.Attribute{Type: unix.NFTA_DYNSET_SET_ID, Data: binaryutil.BigEndian.PutUint32(e.SetID)})
// Per https://git.netfilter.org/libnftnl/tree/src/expr/dynset.c?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n170
if len(e.Exprs) > 0 {
flags |= NFT_DYNSET_F_EXPR
switch len(e.Exprs) {
case 1:
exprData, err := Marshal(fam, e.Exprs[0])
if err != nil {
return nil, err
}
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_DYNSET_EXPR, Data: exprData})
default:
var elemAttrs []netlink.Attribute
for _, ex := range e.Exprs {
exprData, err := Marshal(fam, ex)
if err != nil {
return nil, err
}
elemAttrs = append(elemAttrs, netlink.Attribute{Type: unix.NFTA_LIST_ELEM, Data: exprData})
}
elemData, err := netlink.MarshalAttributes(elemAttrs)
if err != nil {
return nil, err
}
opAttrs = append(opAttrs, netlink.Attribute{Type: NFTA_DYNSET_EXPRESSIONS, Data: elemData})
}
}
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_DYNSET_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)})
opData, err := netlink.MarshalAttributes(opAttrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("dynset\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: opData},
})
}
func (e *Dynset) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_DYNSET_SET_NAME:
e.SetName = ad.String()
case unix.NFTA_DYNSET_SET_ID:
e.SetID = ad.Uint32()
case unix.NFTA_DYNSET_SREG_KEY:
e.SrcRegKey = ad.Uint32()
case unix.NFTA_DYNSET_SREG_DATA:
e.SrcRegData = ad.Uint32()
case unix.NFTA_DYNSET_OP:
e.Operation = ad.Uint32()
case unix.NFTA_DYNSET_TIMEOUT:
e.Timeout = time.Duration(time.Millisecond * time.Duration(ad.Uint64()))
case unix.NFTA_DYNSET_FLAGS:
e.Invert = (ad.Uint32() & unix.NFT_DYNSET_F_INV) != 0
case unix.NFTA_DYNSET_EXPR:
exprs, err := parseexprfunc.ParseExprBytesFunc(fam, ad, ad.Bytes())
if err != nil {
return err
}
e.setInterfaceExprs(exprs)
case NFTA_DYNSET_EXPRESSIONS:
exprs, err := parseexprfunc.ParseExprMsgFunc(fam, ad.Bytes())
if err != nil {
return err
}
e.setInterfaceExprs(exprs)
}
}
return ad.Err()
}
func (e *Dynset) setInterfaceExprs(exprs []interface{}) {
e.Exprs = make([]Any, len(exprs))
for i := range exprs {
e.Exprs[i] = exprs[i].(Any)
}
}

430
vendor/github.com/google/nftables/expr/expr.go generated vendored Normal file
View File

@@ -0,0 +1,430 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package expr provides nftables rule expressions.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/internal/parseexprfunc"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
func init() {
parseexprfunc.ParseExprBytesFunc = func(fam byte, ad *netlink.AttributeDecoder, b []byte) ([]interface{}, error) {
exprs, err := exprsFromBytes(fam, ad, b)
if err != nil {
return nil, err
}
result := make([]interface{}, len(exprs))
for idx, expr := range exprs {
result[idx] = expr
}
return result, nil
}
parseexprfunc.ParseExprMsgFunc = func(fam byte, b []byte) ([]interface{}, error) {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
var exprs []interface{}
for ad.Next() {
e, err := parseexprfunc.ParseExprBytesFunc(fam, ad, b)
if err != nil {
return e, err
}
exprs = append(exprs, e...)
}
return exprs, ad.Err()
}
}
// Marshal serializes the specified expression into a byte slice.
func Marshal(fam byte, e Any) ([]byte, error) {
return e.marshal(fam)
}
// Unmarshal fills an expression from the specified byte slice.
func Unmarshal(fam byte, data []byte, e Any) error {
return e.unmarshal(fam, data)
}
// exprsFromBytes parses nested raw expressions bytes
// to construct nftables expressions
func exprsFromBytes(fam byte, ad *netlink.AttributeDecoder, b []byte) ([]Any, error) {
var exprs []Any
ad.Do(func(b []byte) error {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
var name string
for ad.Next() {
switch ad.Type() {
case unix.NFTA_EXPR_NAME:
name = ad.String()
if name == "notrack" {
e := &Notrack{}
exprs = append(exprs, e)
}
case unix.NFTA_EXPR_DATA:
var e Any
switch name {
case "ct":
e = &Ct{}
case "range":
e = &Range{}
case "meta":
e = &Meta{}
case "cmp":
e = &Cmp{}
case "counter":
e = &Counter{}
case "objref":
e = &Objref{}
case "payload":
e = &Payload{}
case "lookup":
e = &Lookup{}
case "immediate":
e = &Immediate{}
case "bitwise":
e = &Bitwise{}
case "redir":
e = &Redir{}
case "nat":
e = &NAT{}
case "limit":
e = &Limit{}
case "quota":
e = &Quota{}
case "dynset":
e = &Dynset{}
case "log":
e = &Log{}
case "exthdr":
e = &Exthdr{}
case "match":
e = &Match{}
case "target":
e = &Target{}
case "connlimit":
e = &Connlimit{}
case "queue":
e = &Queue{}
case "flow_offload":
e = &FlowOffload{}
case "reject":
e = &Reject{}
case "masq":
e = &Masq{}
case "hash":
e = &Hash{}
}
if e == nil {
// TODO: introduce an opaque expression type so that users know
// something is here.
continue // unsupported expression type
}
ad.Do(func(b []byte) error {
if err := Unmarshal(fam, b, e); err != nil {
return err
}
// Verdict expressions are a special-case of immediate expressions, so
// if the expression is an immediate writing nothing into the verdict
// register (invalid), re-parse it as a verdict expression.
if imm, isImmediate := e.(*Immediate); isImmediate && imm.Register == unix.NFT_REG_VERDICT && len(imm.Data) == 0 {
e = &Verdict{}
if err := Unmarshal(fam, b, e); err != nil {
return err
}
}
exprs = append(exprs, e)
return nil
})
}
}
return ad.Err()
})
return exprs, ad.Err()
}
// Any is an interface implemented by any expression type.
type Any interface {
marshal(fam byte) ([]byte, error)
unmarshal(fam byte, data []byte) error
}
// MetaKey specifies which piece of meta information should be loaded. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation
type MetaKey uint32
// Possible MetaKey values.
const (
MetaKeyLEN MetaKey = unix.NFT_META_LEN
MetaKeyPROTOCOL MetaKey = unix.NFT_META_PROTOCOL
MetaKeyPRIORITY MetaKey = unix.NFT_META_PRIORITY
MetaKeyMARK MetaKey = unix.NFT_META_MARK
MetaKeyIIF MetaKey = unix.NFT_META_IIF
MetaKeyOIF MetaKey = unix.NFT_META_OIF
MetaKeyIIFNAME MetaKey = unix.NFT_META_IIFNAME
MetaKeyOIFNAME MetaKey = unix.NFT_META_OIFNAME
MetaKeyIIFTYPE MetaKey = unix.NFT_META_IIFTYPE
MetaKeyOIFTYPE MetaKey = unix.NFT_META_OIFTYPE
MetaKeySKUID MetaKey = unix.NFT_META_SKUID
MetaKeySKGID MetaKey = unix.NFT_META_SKGID
MetaKeyNFTRACE MetaKey = unix.NFT_META_NFTRACE
MetaKeyRTCLASSID MetaKey = unix.NFT_META_RTCLASSID
MetaKeySECMARK MetaKey = unix.NFT_META_SECMARK
MetaKeyNFPROTO MetaKey = unix.NFT_META_NFPROTO
MetaKeyL4PROTO MetaKey = unix.NFT_META_L4PROTO
MetaKeyBRIIIFNAME MetaKey = unix.NFT_META_BRI_IIFNAME
MetaKeyBRIOIFNAME MetaKey = unix.NFT_META_BRI_OIFNAME
MetaKeyPKTTYPE MetaKey = unix.NFT_META_PKTTYPE
MetaKeyCPU MetaKey = unix.NFT_META_CPU
MetaKeyIIFGROUP MetaKey = unix.NFT_META_IIFGROUP
MetaKeyOIFGROUP MetaKey = unix.NFT_META_OIFGROUP
MetaKeyCGROUP MetaKey = unix.NFT_META_CGROUP
MetaKeyPRANDOM MetaKey = unix.NFT_META_PRANDOM
)
// Meta loads packet meta information for later comparisons. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation
type Meta struct {
Key MetaKey
SourceRegister bool
Register uint32
}
func (e *Meta) marshal(fam byte) ([]byte, error) {
regData := []byte{}
exprData, err := netlink.MarshalAttributes(
[]netlink.Attribute{
{Type: unix.NFTA_META_KEY, Data: binaryutil.BigEndian.PutUint32(uint32(e.Key))},
},
)
if err != nil {
return nil, err
}
if e.SourceRegister {
regData, err = netlink.MarshalAttributes(
[]netlink.Attribute{
{Type: unix.NFTA_META_SREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
},
)
} else {
regData, err = netlink.MarshalAttributes(
[]netlink.Attribute{
{Type: unix.NFTA_META_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
},
)
}
if err != nil {
return nil, err
}
exprData = append(exprData, regData...)
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("meta\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: exprData},
})
}
func (e *Meta) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_META_SREG:
e.Register = ad.Uint32()
e.SourceRegister = true
case unix.NFTA_META_DREG:
e.Register = ad.Uint32()
case unix.NFTA_META_KEY:
e.Key = MetaKey(ad.Uint32())
}
}
return ad.Err()
}
// Masq (Masquerade) is a special case of SNAT, where the source address is
// automagically set to the address of the output interface. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)#Masquerading
type Masq struct {
Random bool
FullyRandom bool
Persistent bool
ToPorts bool
RegProtoMin uint32
RegProtoMax uint32
}
const (
// NF_NAT_RANGE_PROTO_RANDOM defines flag for a random masquerade
NF_NAT_RANGE_PROTO_RANDOM = unix.NF_NAT_RANGE_PROTO_RANDOM
// NF_NAT_RANGE_PROTO_RANDOM_FULLY defines flag for a fully random masquerade
NF_NAT_RANGE_PROTO_RANDOM_FULLY = unix.NF_NAT_RANGE_PROTO_RANDOM_FULLY
// NF_NAT_RANGE_PERSISTENT defines flag for a persistent masquerade
NF_NAT_RANGE_PERSISTENT = unix.NF_NAT_RANGE_PERSISTENT
// NF_NAT_RANGE_PREFIX defines flag for a prefix masquerade
NF_NAT_RANGE_PREFIX = unix.NF_NAT_RANGE_NETMAP
)
func (e *Masq) marshal(fam byte) ([]byte, error) {
msgData := []byte{}
if !e.ToPorts {
flags := uint32(0)
if e.Random {
flags |= NF_NAT_RANGE_PROTO_RANDOM
}
if e.FullyRandom {
flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY
}
if e.Persistent {
flags |= NF_NAT_RANGE_PERSISTENT
}
if flags != 0 {
flagsData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_MASQ_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)}})
if err != nil {
return nil, err
}
msgData = append(msgData, flagsData...)
}
} else {
regsData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_MASQ_REG_PROTO_MIN, Data: binaryutil.BigEndian.PutUint32(e.RegProtoMin)}})
if err != nil {
return nil, err
}
msgData = append(msgData, regsData...)
if e.RegProtoMax != 0 {
regsData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_MASQ_REG_PROTO_MAX, Data: binaryutil.BigEndian.PutUint32(e.RegProtoMax)}})
if err != nil {
return nil, err
}
msgData = append(msgData, regsData...)
}
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("masq\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: msgData},
})
}
func (e *Masq) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_MASQ_REG_PROTO_MIN:
e.ToPorts = true
e.RegProtoMin = ad.Uint32()
case unix.NFTA_MASQ_REG_PROTO_MAX:
e.RegProtoMax = ad.Uint32()
case unix.NFTA_MASQ_FLAGS:
flags := ad.Uint32()
e.Persistent = (flags & NF_NAT_RANGE_PERSISTENT) != 0
e.Random = (flags & NF_NAT_RANGE_PROTO_RANDOM) != 0
e.FullyRandom = (flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) != 0
}
}
return ad.Err()
}
// CmpOp specifies which type of comparison should be performed.
type CmpOp uint32
// Possible CmpOp values.
const (
CmpOpEq CmpOp = unix.NFT_CMP_EQ
CmpOpNeq CmpOp = unix.NFT_CMP_NEQ
CmpOpLt CmpOp = unix.NFT_CMP_LT
CmpOpLte CmpOp = unix.NFT_CMP_LTE
CmpOpGt CmpOp = unix.NFT_CMP_GT
CmpOpGte CmpOp = unix.NFT_CMP_GTE
)
// Cmp compares a register with the specified data.
type Cmp struct {
Op CmpOp
Register uint32
Data []byte
}
func (e *Cmp) marshal(fam byte) ([]byte, error) {
cmpData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: e.Data},
})
if err != nil {
return nil, err
}
exprData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_CMP_SREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
{Type: unix.NFTA_CMP_OP, Data: binaryutil.BigEndian.PutUint32(uint32(e.Op))},
{Type: unix.NLA_F_NESTED | unix.NFTA_CMP_DATA, Data: cmpData},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("cmp\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: exprData},
})
}
func (e *Cmp) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_CMP_SREG:
e.Register = ad.Uint32()
case unix.NFTA_CMP_OP:
e.Op = CmpOp(ad.Uint32())
case unix.NFTA_CMP_DATA:
ad.Do(func(b []byte) error {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
if ad.Next() && ad.Type() == unix.NFTA_DATA_VALUE {
ad.Do(func(b []byte) error {
e.Data = b
return nil
})
}
return ad.Err()
})
}
}
return ad.Err()
}

102
vendor/github.com/google/nftables/expr/exthdr.go generated vendored Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type ExthdrOp uint32
const (
ExthdrOpIpv6 ExthdrOp = unix.NFT_EXTHDR_OP_IPV6
ExthdrOpTcpopt ExthdrOp = unix.NFT_EXTHDR_OP_TCPOPT
)
type Exthdr struct {
DestRegister uint32
Type uint8
Offset uint32
Len uint32
Flags uint32
Op ExthdrOp
SourceRegister uint32
}
func (e *Exthdr) marshal(fam byte) ([]byte, error) {
var attr []netlink.Attribute
// Operations are differentiated by the Op and whether the SourceRegister
// or DestRegister is set. Mixing them results in EOPNOTSUPP.
if e.SourceRegister != 0 {
attr = []netlink.Attribute{
{Type: unix.NFTA_EXTHDR_SREG, Data: binaryutil.BigEndian.PutUint32(e.SourceRegister)}}
} else {
attr = []netlink.Attribute{
{Type: unix.NFTA_EXTHDR_DREG, Data: binaryutil.BigEndian.PutUint32(e.DestRegister)}}
}
attr = append(attr,
netlink.Attribute{Type: unix.NFTA_EXTHDR_TYPE, Data: []byte{e.Type}},
netlink.Attribute{Type: unix.NFTA_EXTHDR_OFFSET, Data: binaryutil.BigEndian.PutUint32(e.Offset)},
netlink.Attribute{Type: unix.NFTA_EXTHDR_LEN, Data: binaryutil.BigEndian.PutUint32(e.Len)},
netlink.Attribute{Type: unix.NFTA_EXTHDR_OP, Data: binaryutil.BigEndian.PutUint32(uint32(e.Op))})
// Flags is only set if DREG is set
if e.DestRegister != 0 {
attr = append(attr,
netlink.Attribute{Type: unix.NFTA_EXTHDR_FLAGS, Data: binaryutil.BigEndian.PutUint32(e.Flags)})
}
data, err := netlink.MarshalAttributes(attr)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("exthdr\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Exthdr) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_EXTHDR_DREG:
e.DestRegister = ad.Uint32()
case unix.NFTA_EXTHDR_TYPE:
e.Type = ad.Uint8()
case unix.NFTA_EXTHDR_OFFSET:
e.Offset = ad.Uint32()
case unix.NFTA_EXTHDR_LEN:
e.Len = ad.Uint32()
case unix.NFTA_EXTHDR_FLAGS:
e.Flags = ad.Uint32()
case unix.NFTA_EXTHDR_OP:
e.Op = ExthdrOp(ad.Uint32())
case unix.NFTA_EXTHDR_SREG:
e.SourceRegister = ad.Uint32()
}
}
return ad.Err()
}

128
vendor/github.com/google/nftables/expr/fib.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// Fib defines fib expression structure
type Fib struct {
Register uint32
ResultOIF bool
ResultOIFNAME bool
ResultADDRTYPE bool
FlagSADDR bool
FlagDADDR bool
FlagMARK bool
FlagIIF bool
FlagOIF bool
FlagPRESENT bool
}
func (e *Fib) marshal(fam byte) ([]byte, error) {
data := []byte{}
reg, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_FIB_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
})
if err != nil {
return nil, err
}
data = append(data, reg...)
flags := uint32(0)
if e.FlagSADDR {
flags |= unix.NFTA_FIB_F_SADDR
}
if e.FlagDADDR {
flags |= unix.NFTA_FIB_F_DADDR
}
if e.FlagMARK {
flags |= unix.NFTA_FIB_F_MARK
}
if e.FlagIIF {
flags |= unix.NFTA_FIB_F_IIF
}
if e.FlagOIF {
flags |= unix.NFTA_FIB_F_OIF
}
if e.FlagPRESENT {
flags |= unix.NFTA_FIB_F_PRESENT
}
if flags != 0 {
flg, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_FIB_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)},
})
if err != nil {
return nil, err
}
data = append(data, flg...)
}
results := uint32(0)
if e.ResultOIF {
results |= unix.NFT_FIB_RESULT_OIF
}
if e.ResultOIFNAME {
results |= unix.NFT_FIB_RESULT_OIFNAME
}
if e.ResultADDRTYPE {
results |= unix.NFT_FIB_RESULT_ADDRTYPE
}
if results != 0 {
rslt, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_FIB_RESULT, Data: binaryutil.BigEndian.PutUint32(results)},
})
if err != nil {
return nil, err
}
data = append(data, rslt...)
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("fib\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Fib) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_FIB_DREG:
e.Register = ad.Uint32()
case unix.NFTA_FIB_RESULT:
result := ad.Uint32()
e.ResultOIF = (result & unix.NFT_FIB_RESULT_OIF) == 1
e.ResultOIFNAME = (result & unix.NFT_FIB_RESULT_OIFNAME) == 1
e.ResultADDRTYPE = (result & unix.NFT_FIB_RESULT_ADDRTYPE) == 1
case unix.NFTA_FIB_FLAGS:
flags := ad.Uint32()
e.FlagSADDR = (flags & unix.NFTA_FIB_F_SADDR) == 1
e.FlagDADDR = (flags & unix.NFTA_FIB_F_DADDR) == 1
e.FlagMARK = (flags & unix.NFTA_FIB_F_MARK) == 1
e.FlagIIF = (flags & unix.NFTA_FIB_F_IIF) == 1
e.FlagOIF = (flags & unix.NFTA_FIB_F_OIF) == 1
e.FlagPRESENT = (flags & unix.NFTA_FIB_F_PRESENT) == 1
}
}
return ad.Err()
}

59
vendor/github.com/google/nftables/expr/flow_offload.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
const NFTNL_EXPR_FLOW_TABLE_NAME = 1
type FlowOffload struct {
Name string
}
func (e *FlowOffload) marshal(fam byte) ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: NFTNL_EXPR_FLOW_TABLE_NAME, Data: []byte(e.Name)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("flow_offload\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *FlowOffload) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case NFTNL_EXPR_FLOW_TABLE_NAME:
e.Name = ad.String()
}
}
return ad.Err()
}

94
vendor/github.com/google/nftables/expr/hash.go generated vendored Normal file
View File

@@ -0,0 +1,94 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type HashType uint32
const (
HashTypeJenkins HashType = unix.NFT_HASH_JENKINS
HashTypeSym HashType = unix.NFT_HASH_SYM
)
// Hash defines type for nftables internal hashing functions
type Hash struct {
SourceRegister uint32
DestRegister uint32
Length uint32
Modulus uint32
Seed uint32
Offset uint32
Type HashType
}
func (e *Hash) marshal(fam byte) ([]byte, error) {
hashAttrs := []netlink.Attribute{
{Type: unix.NFTA_HASH_SREG, Data: binaryutil.BigEndian.PutUint32(uint32(e.SourceRegister))},
{Type: unix.NFTA_HASH_DREG, Data: binaryutil.BigEndian.PutUint32(uint32(e.DestRegister))},
{Type: unix.NFTA_HASH_LEN, Data: binaryutil.BigEndian.PutUint32(uint32(e.Length))},
{Type: unix.NFTA_HASH_MODULUS, Data: binaryutil.BigEndian.PutUint32(uint32(e.Modulus))},
}
if e.Seed != 0 {
hashAttrs = append(hashAttrs, netlink.Attribute{
Type: unix.NFTA_HASH_SEED, Data: binaryutil.BigEndian.PutUint32(uint32(e.Seed)),
})
}
hashAttrs = append(hashAttrs, []netlink.Attribute{
{Type: unix.NFTA_HASH_OFFSET, Data: binaryutil.BigEndian.PutUint32(uint32(e.Offset))},
{Type: unix.NFTA_HASH_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(e.Type))},
}...)
data, err := netlink.MarshalAttributes(hashAttrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("hash\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Hash) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_HASH_SREG:
e.SourceRegister = ad.Uint32()
case unix.NFTA_HASH_DREG:
e.DestRegister = ad.Uint32()
case unix.NFTA_HASH_LEN:
e.Length = ad.Uint32()
case unix.NFTA_HASH_MODULUS:
e.Modulus = ad.Uint32()
case unix.NFTA_HASH_SEED:
e.Seed = ad.Uint32()
case unix.NFTA_HASH_OFFSET:
e.Offset = ad.Uint32()
case unix.NFTA_HASH_TYPE:
e.Type = HashType(ad.Uint32())
}
}
return ad.Err()
}

79
vendor/github.com/google/nftables/expr/immediate.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Immediate struct {
Register uint32
Data []byte
}
func (e *Immediate) marshal(fam byte) ([]byte, error) {
immData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: e.Data},
})
if err != nil {
return nil, err
}
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_IMMEDIATE_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
{Type: unix.NLA_F_NESTED | unix.NFTA_IMMEDIATE_DATA, Data: immData},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("immediate\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Immediate) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_IMMEDIATE_DREG:
e.Register = ad.Uint32()
case unix.NFTA_IMMEDIATE_DATA:
nestedAD, err := netlink.NewAttributeDecoder(ad.Bytes())
if err != nil {
return fmt.Errorf("nested NewAttributeDecoder() failed: %v", err)
}
for nestedAD.Next() {
switch nestedAD.Type() {
case unix.NFTA_DATA_VALUE:
e.Data = nestedAD.Bytes()
}
}
if nestedAD.Err() != nil {
return fmt.Errorf("decoding immediate: %v", nestedAD.Err())
}
}
}
return ad.Err()
}

128
vendor/github.com/google/nftables/expr/limit.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// LimitType represents the type of the limit expression.
type LimitType uint32
// Imported from the nft_limit_type enum in netfilter/nf_tables.h.
const (
LimitTypePkts LimitType = unix.NFT_LIMIT_PKTS
LimitTypePktBytes LimitType = unix.NFT_LIMIT_PKT_BYTES
)
// LimitTime represents the limit unit.
type LimitTime uint64
// Possible limit unit values.
const (
LimitTimeSecond LimitTime = 1
LimitTimeMinute LimitTime = 60
LimitTimeHour LimitTime = 60 * 60
LimitTimeDay LimitTime = 60 * 60 * 24
LimitTimeWeek LimitTime = 60 * 60 * 24 * 7
)
func limitTime(value uint64) (LimitTime, error) {
switch LimitTime(value) {
case LimitTimeSecond:
return LimitTimeSecond, nil
case LimitTimeMinute:
return LimitTimeMinute, nil
case LimitTimeHour:
return LimitTimeHour, nil
case LimitTimeDay:
return LimitTimeDay, nil
case LimitTimeWeek:
return LimitTimeWeek, nil
default:
return 0, fmt.Errorf("expr: invalid limit unit value %d", value)
}
}
// Limit represents a rate limit expression.
type Limit struct {
Type LimitType
Rate uint64
Over bool
Unit LimitTime
Burst uint32
}
func (l *Limit) marshal(fam byte) ([]byte, error) {
var flags uint32
if l.Over {
flags = unix.NFT_LIMIT_F_INV
}
attrs := []netlink.Attribute{
{Type: unix.NFTA_LIMIT_RATE, Data: binaryutil.BigEndian.PutUint64(l.Rate)},
{Type: unix.NFTA_LIMIT_UNIT, Data: binaryutil.BigEndian.PutUint64(uint64(l.Unit))},
{Type: unix.NFTA_LIMIT_BURST, Data: binaryutil.BigEndian.PutUint32(l.Burst)},
{Type: unix.NFTA_LIMIT_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(l.Type))},
{Type: unix.NFTA_LIMIT_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)},
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("limit\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (l *Limit) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_LIMIT_RATE:
l.Rate = ad.Uint64()
case unix.NFTA_LIMIT_UNIT:
l.Unit, err = limitTime(ad.Uint64())
if err != nil {
return err
}
case unix.NFTA_LIMIT_BURST:
l.Burst = ad.Uint32()
case unix.NFTA_LIMIT_TYPE:
l.Type = LimitType(ad.Uint32())
if l.Type != LimitTypePkts && l.Type != LimitTypePktBytes {
return fmt.Errorf("expr: invalid limit type %d", l.Type)
}
case unix.NFTA_LIMIT_FLAGS:
l.Over = (ad.Uint32() & unix.NFT_LIMIT_F_INV) == 1
default:
return errors.New("expr: unhandled limit netlink attribute")
}
}
return ad.Err()
}

150
vendor/github.com/google/nftables/expr/log.go generated vendored Normal file
View File

@@ -0,0 +1,150 @@
// Copyright 2019 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type LogLevel uint32
const (
// See https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=5b364657a35f4e4cd5d220ba2a45303d729c8eca#n1226
LogLevelEmerg LogLevel = iota
LogLevelAlert
LogLevelCrit
LogLevelErr
LogLevelWarning
LogLevelNotice
LogLevelInfo
LogLevelDebug
LogLevelAudit
)
type LogFlags uint32
const (
// See https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_log.h?id=5b364657a35f4e4cd5d220ba2a45303d729c8eca
LogFlagsTCPSeq LogFlags = 0x01 << iota
LogFlagsTCPOpt
LogFlagsIPOpt
LogFlagsUID
LogFlagsNFLog
LogFlagsMACDecode
LogFlagsMask LogFlags = 0x2f
)
// Log defines type for NFT logging
// See https://git.netfilter.org/libnftnl/tree/src/expr/log.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n25
type Log struct {
Level LogLevel
// Refers to log flags (flags all, flags ip options, ...)
Flags LogFlags
// Equivalent to expression flags.
// Indicates that an option is set by setting a bit
// on index referred by the NFTA_LOG_* value.
// See https://cs.opensource.google/go/x/sys/+/3681064d:unix/ztypes_linux.go;l=2126;drc=3681064d51587c1db0324b3d5c23c2ddbcff6e8f
Key uint32
Snaplen uint32
Group uint16
QThreshold uint16
// Log prefix string content
Data []byte
}
func (e *Log) marshal(fam byte) ([]byte, error) {
// Per https://git.netfilter.org/libnftnl/tree/src/expr/log.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n129
attrs := make([]netlink.Attribute, 0)
if e.Key&(1<<unix.NFTA_LOG_GROUP) != 0 {
attrs = append(attrs, netlink.Attribute{
Type: unix.NFTA_LOG_GROUP,
Data: binaryutil.BigEndian.PutUint16(e.Group),
})
}
if e.Key&(1<<unix.NFTA_LOG_PREFIX) != 0 {
prefix := append(e.Data, '\x00')
attrs = append(attrs, netlink.Attribute{
Type: unix.NFTA_LOG_PREFIX,
Data: prefix,
})
}
if e.Key&(1<<unix.NFTA_LOG_SNAPLEN) != 0 {
attrs = append(attrs, netlink.Attribute{
Type: unix.NFTA_LOG_SNAPLEN,
Data: binaryutil.BigEndian.PutUint32(e.Snaplen),
})
}
if e.Key&(1<<unix.NFTA_LOG_QTHRESHOLD) != 0 {
attrs = append(attrs, netlink.Attribute{
Type: unix.NFTA_LOG_QTHRESHOLD,
Data: binaryutil.BigEndian.PutUint16(e.QThreshold),
})
}
if e.Key&(1<<unix.NFTA_LOG_LEVEL) != 0 {
attrs = append(attrs, netlink.Attribute{
Type: unix.NFTA_LOG_LEVEL,
Data: binaryutil.BigEndian.PutUint32(uint32(e.Level)),
})
}
if e.Key&(1<<unix.NFTA_LOG_FLAGS) != 0 {
attrs = append(attrs, netlink.Attribute{
Type: unix.NFTA_LOG_FLAGS,
Data: binaryutil.BigEndian.PutUint32(uint32(e.Flags)),
})
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("log\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Log) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
e.Key |= 1 << uint32(ad.Type())
data := ad.Bytes()
switch ad.Type() {
case unix.NFTA_LOG_GROUP:
e.Group = binaryutil.BigEndian.Uint16(data)
case unix.NFTA_LOG_PREFIX:
// Getting rid of \x00 at the end of string
e.Data = data[:len(data)-1]
case unix.NFTA_LOG_SNAPLEN:
e.Snaplen = binaryutil.BigEndian.Uint32(data)
case unix.NFTA_LOG_QTHRESHOLD:
e.QThreshold = binaryutil.BigEndian.Uint16(data)
case unix.NFTA_LOG_LEVEL:
e.Level = LogLevel(binaryutil.BigEndian.Uint32(data))
case unix.NFTA_LOG_FLAGS:
e.Flags = LogFlags(binaryutil.BigEndian.Uint32(data))
}
}
return ad.Err()
}

85
vendor/github.com/google/nftables/expr/lookup.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// Lookup represents a match against the contents of a set.
type Lookup struct {
SourceRegister uint32
DestRegister uint32
IsDestRegSet bool
SetID uint32
SetName string
Invert bool
}
func (e *Lookup) marshal(fam byte) ([]byte, error) {
// See: https://git.netfilter.org/libnftnl/tree/src/expr/lookup.c?id=6dc1c3d8bb64077da7f3f28c7368fb087d10a492#n115
var opAttrs []netlink.Attribute
if e.SourceRegister != 0 {
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_LOOKUP_SREG, Data: binaryutil.BigEndian.PutUint32(e.SourceRegister)})
}
if e.IsDestRegSet {
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_LOOKUP_DREG, Data: binaryutil.BigEndian.PutUint32(e.DestRegister)})
}
if e.Invert {
opAttrs = append(opAttrs, netlink.Attribute{Type: unix.NFTA_LOOKUP_FLAGS, Data: binaryutil.BigEndian.PutUint32(unix.NFT_LOOKUP_F_INV)})
}
opAttrs = append(opAttrs,
netlink.Attribute{Type: unix.NFTA_LOOKUP_SET, Data: []byte(e.SetName + "\x00")},
netlink.Attribute{Type: unix.NFTA_LOOKUP_SET_ID, Data: binaryutil.BigEndian.PutUint32(e.SetID)},
)
opData, err := netlink.MarshalAttributes(opAttrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("lookup\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: opData},
})
}
func (e *Lookup) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_LOOKUP_SET:
e.SetName = ad.String()
case unix.NFTA_LOOKUP_SET_ID:
e.SetID = ad.Uint32()
case unix.NFTA_LOOKUP_SREG:
e.SourceRegister = ad.Uint32()
case unix.NFTA_LOOKUP_DREG:
e.DestRegister = ad.Uint32()
e.IsDestRegSet = true
case unix.NFTA_LOOKUP_FLAGS:
e.Invert = (ad.Uint32() & unix.NFT_LOOKUP_F_INV) != 0
}
}
return ad.Err()
}

75
vendor/github.com/google/nftables/expr/match.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
package expr
import (
"bytes"
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/xt"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// See https://git.netfilter.org/libnftnl/tree/src/expr/match.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n30
type Match struct {
Name string
Rev uint32
Info xt.InfoAny
}
func (e *Match) marshal(fam byte) ([]byte, error) {
// Per https://git.netfilter.org/libnftnl/tree/src/expr/match.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n38
name := e.Name
// limit the extension name as (some) user-space tools do and leave room for
// trailing \x00
if len(name) >= /* sic! */ XTablesExtensionNameMaxLen {
name = name[:XTablesExtensionNameMaxLen-1] // leave room for trailing \x00.
}
// Marshalling assumes that the correct Info type for the particular table
// family and Match revision has been set.
info, err := xt.Marshal(xt.TableFamily(fam), e.Rev, e.Info)
if err != nil {
return nil, err
}
attrs := []netlink.Attribute{
{Type: unix.NFTA_MATCH_NAME, Data: []byte(name + "\x00")},
{Type: unix.NFTA_MATCH_REV, Data: binaryutil.BigEndian.PutUint32(e.Rev)},
{Type: unix.NFTA_MATCH_INFO, Data: info},
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("match\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Match) unmarshal(fam byte, data []byte) error {
// Per https://git.netfilter.org/libnftnl/tree/src/expr/match.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n65
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
var info []byte
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_MATCH_NAME:
// We are forgiving here, accepting any length and even missing terminating \x00.
e.Name = string(bytes.TrimRight(ad.Bytes(), "\x00"))
case unix.NFTA_MATCH_REV:
e.Rev = ad.Uint32()
case unix.NFTA_MATCH_INFO:
info = ad.Bytes()
}
}
if err = ad.Err(); err != nil {
return err
}
e.Info, err = xt.Unmarshal(e.Name, xt.TableFamily(fam), e.Rev, info)
return err
}

132
vendor/github.com/google/nftables/expr/nat.go generated vendored Normal file
View File

@@ -0,0 +1,132 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type NATType uint32
// Possible NATType values.
const (
NATTypeSourceNAT NATType = unix.NFT_NAT_SNAT
NATTypeDestNAT NATType = unix.NFT_NAT_DNAT
)
type NAT struct {
Type NATType
Family uint32 // TODO: typed const
RegAddrMin uint32
RegAddrMax uint32
RegProtoMin uint32
RegProtoMax uint32
Random bool
FullyRandom bool
Persistent bool
Prefix bool
}
// |00048|N-|00001| |len |flags| type|
// |00008|--|00001| |len |flags| type|
// | 6e 61 74 00 | | data | n a t
// |00036|N-|00002| |len |flags| type|
// |00008|--|00001| |len |flags| type| NFTA_NAT_TYPE
// | 00 00 00 01 | | data | NFT_NAT_DNAT
// |00008|--|00002| |len |flags| type| NFTA_NAT_FAMILY
// | 00 00 00 02 | | data | NFPROTO_IPV4
// |00008|--|00003| |len |flags| type| NFTA_NAT_REG_ADDR_MIN
// | 00 00 00 01 | | data | reg 1
// |00008|--|00005| |len |flags| type| NFTA_NAT_REG_PROTO_MIN
// | 00 00 00 02 | | data | reg 2
func (e *NAT) marshal(fam byte) ([]byte, error) {
attrs := []netlink.Attribute{
{Type: unix.NFTA_NAT_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(e.Type))},
{Type: unix.NFTA_NAT_FAMILY, Data: binaryutil.BigEndian.PutUint32(e.Family)},
}
if e.RegAddrMin != 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_NAT_REG_ADDR_MIN, Data: binaryutil.BigEndian.PutUint32(e.RegAddrMin)})
if e.RegAddrMax != 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_NAT_REG_ADDR_MAX, Data: binaryutil.BigEndian.PutUint32(e.RegAddrMax)})
}
}
if e.RegProtoMin != 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_NAT_REG_PROTO_MIN, Data: binaryutil.BigEndian.PutUint32(e.RegProtoMin)})
if e.RegProtoMax != 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_NAT_REG_PROTO_MAX, Data: binaryutil.BigEndian.PutUint32(e.RegProtoMax)})
}
}
flags := uint32(0)
if e.Random {
flags |= NF_NAT_RANGE_PROTO_RANDOM
}
if e.FullyRandom {
flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY
}
if e.Persistent {
flags |= NF_NAT_RANGE_PERSISTENT
}
if e.Prefix {
flags |= NF_NAT_RANGE_PREFIX
}
if flags != 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_NAT_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)})
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("nat\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *NAT) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_NAT_TYPE:
e.Type = NATType(ad.Uint32())
case unix.NFTA_NAT_FAMILY:
e.Family = ad.Uint32()
case unix.NFTA_NAT_REG_ADDR_MIN:
e.RegAddrMin = ad.Uint32()
case unix.NFTA_NAT_REG_ADDR_MAX:
e.RegAddrMax = ad.Uint32()
case unix.NFTA_NAT_REG_PROTO_MIN:
e.RegProtoMin = ad.Uint32()
case unix.NFTA_NAT_REG_PROTO_MAX:
e.RegProtoMax = ad.Uint32()
case unix.NFTA_NAT_FLAGS:
flags := ad.Uint32()
e.Persistent = (flags & NF_NAT_RANGE_PERSISTENT) != 0
e.Random = (flags & NF_NAT_RANGE_PROTO_RANDOM) != 0
e.FullyRandom = (flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) != 0
e.Prefix = (flags & NF_NAT_RANGE_PREFIX) != 0
}
}
return ad.Err()
}

38
vendor/github.com/google/nftables/expr/notrack.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2019 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Notrack struct{}
func (e *Notrack) marshal(fam byte) ([]byte, error) {
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("notrack\x00")},
})
}
func (e *Notrack) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
return ad.Err()
}

78
vendor/github.com/google/nftables/expr/numgen.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// Numgen defines Numgen expression structure
type Numgen struct {
Register uint32
Modulus uint32
Type uint32
Offset uint32
}
func (e *Numgen) marshal(fam byte) ([]byte, error) {
// Currently only two types are supported, failing if Type is not of two known types
switch e.Type {
case unix.NFT_NG_INCREMENTAL:
case unix.NFT_NG_RANDOM:
default:
return nil, fmt.Errorf("unsupported numgen type %d", e.Type)
}
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_NG_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
{Type: unix.NFTA_NG_MODULUS, Data: binaryutil.BigEndian.PutUint32(e.Modulus)},
{Type: unix.NFTA_NG_TYPE, Data: binaryutil.BigEndian.PutUint32(e.Type)},
{Type: unix.NFTA_NG_OFFSET, Data: binaryutil.BigEndian.PutUint32(e.Offset)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("numgen\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Numgen) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_NG_DREG:
e.Register = ad.Uint32()
case unix.NFTA_NG_MODULUS:
e.Modulus = ad.Uint32()
case unix.NFTA_NG_TYPE:
e.Type = ad.Uint32()
case unix.NFTA_NG_OFFSET:
e.Offset = ad.Uint32()
}
}
return ad.Err()
}

60
vendor/github.com/google/nftables/expr/objref.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Objref struct {
Type int // TODO: enum
Name string
}
func (e *Objref) marshal(fam byte) ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_OBJREF_IMM_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(e.Type))},
{Type: unix.NFTA_OBJREF_IMM_NAME, Data: []byte(e.Name)}, // NOT \x00-terminated?!
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("objref\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Objref) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_OBJREF_IMM_TYPE:
e.Type = int(ad.Uint32())
case unix.NFTA_OBJREF_IMM_NAME:
e.Name = ad.String()
}
}
return ad.Err()
}

131
vendor/github.com/google/nftables/expr/payload.go generated vendored Normal file
View File

@@ -0,0 +1,131 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type PayloadBase uint32
type PayloadCsumType uint32
type PayloadOperationType uint32
// Possible PayloadBase values.
const (
PayloadBaseLLHeader PayloadBase = unix.NFT_PAYLOAD_LL_HEADER
PayloadBaseNetworkHeader PayloadBase = unix.NFT_PAYLOAD_NETWORK_HEADER
PayloadBaseTransportHeader PayloadBase = unix.NFT_PAYLOAD_TRANSPORT_HEADER
)
// Possible PayloadCsumType values.
const (
CsumTypeNone PayloadCsumType = unix.NFT_PAYLOAD_CSUM_NONE
CsumTypeInet PayloadCsumType = unix.NFT_PAYLOAD_CSUM_INET
)
// Possible PayloadOperationType values.
const (
PayloadLoad PayloadOperationType = iota
PayloadWrite
)
type Payload struct {
OperationType PayloadOperationType
DestRegister uint32
SourceRegister uint32
Base PayloadBase
Offset uint32
Len uint32
CsumType PayloadCsumType
CsumOffset uint32
CsumFlags uint32
}
func (e *Payload) marshal(fam byte) ([]byte, error) {
var attrs []netlink.Attribute
if e.OperationType == PayloadWrite {
attrs = []netlink.Attribute{
{Type: unix.NFTA_PAYLOAD_SREG, Data: binaryutil.BigEndian.PutUint32(e.SourceRegister)},
}
} else {
attrs = []netlink.Attribute{
{Type: unix.NFTA_PAYLOAD_DREG, Data: binaryutil.BigEndian.PutUint32(e.DestRegister)},
}
}
attrs = append(attrs,
netlink.Attribute{Type: unix.NFTA_PAYLOAD_BASE, Data: binaryutil.BigEndian.PutUint32(uint32(e.Base))},
netlink.Attribute{Type: unix.NFTA_PAYLOAD_OFFSET, Data: binaryutil.BigEndian.PutUint32(e.Offset)},
netlink.Attribute{Type: unix.NFTA_PAYLOAD_LEN, Data: binaryutil.BigEndian.PutUint32(e.Len)},
)
if e.CsumType > 0 {
attrs = append(attrs,
netlink.Attribute{Type: unix.NFTA_PAYLOAD_CSUM_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(e.CsumType))},
netlink.Attribute{Type: unix.NFTA_PAYLOAD_CSUM_OFFSET, Data: binaryutil.BigEndian.PutUint32(uint32(e.CsumOffset))},
)
if e.CsumFlags > 0 {
attrs = append(attrs,
netlink.Attribute{Type: unix.NFTA_PAYLOAD_CSUM_FLAGS, Data: binaryutil.BigEndian.PutUint32(e.CsumFlags)},
)
}
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("payload\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Payload) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_PAYLOAD_DREG:
e.DestRegister = ad.Uint32()
case unix.NFTA_PAYLOAD_SREG:
e.SourceRegister = ad.Uint32()
e.OperationType = PayloadWrite
case unix.NFTA_PAYLOAD_BASE:
e.Base = PayloadBase(ad.Uint32())
case unix.NFTA_PAYLOAD_OFFSET:
e.Offset = ad.Uint32()
case unix.NFTA_PAYLOAD_LEN:
e.Len = ad.Uint32()
case unix.NFTA_PAYLOAD_CSUM_TYPE:
e.CsumType = PayloadCsumType(ad.Uint32())
case unix.NFTA_PAYLOAD_CSUM_OFFSET:
e.CsumOffset = ad.Uint32()
case unix.NFTA_PAYLOAD_CSUM_FLAGS:
e.CsumFlags = ad.Uint32()
}
}
return ad.Err()
}

81
vendor/github.com/google/nftables/expr/queue.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type QueueAttribute uint16
type QueueFlag uint16
// Possible QueueAttribute values
const (
QueueNum QueueAttribute = unix.NFTA_QUEUE_NUM
QueueTotal QueueAttribute = unix.NFTA_QUEUE_TOTAL
QueueFlags QueueAttribute = unix.NFTA_QUEUE_FLAGS
QueueFlagBypass QueueFlag = unix.NFT_QUEUE_FLAG_BYPASS
QueueFlagFanout QueueFlag = unix.NFT_QUEUE_FLAG_CPU_FANOUT
QueueFlagMask QueueFlag = unix.NFT_QUEUE_FLAG_MASK
)
type Queue struct {
Num uint16
Total uint16
Flag QueueFlag
}
func (e *Queue) marshal(fam byte) ([]byte, error) {
if e.Total == 0 {
e.Total = 1 // The total default value is 1
}
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_QUEUE_NUM, Data: binaryutil.BigEndian.PutUint16(e.Num)},
{Type: unix.NFTA_QUEUE_TOTAL, Data: binaryutil.BigEndian.PutUint16(e.Total)},
{Type: unix.NFTA_QUEUE_FLAGS, Data: binaryutil.BigEndian.PutUint16(uint16(e.Flag))},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("queue\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Queue) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_QUEUE_NUM:
e.Num = ad.Uint16()
case unix.NFTA_QUEUE_TOTAL:
e.Total = ad.Uint16()
case unix.NFTA_QUEUE_FLAGS:
e.Flag = QueueFlag(ad.Uint16())
}
}
return ad.Err()
}

76
vendor/github.com/google/nftables/expr/quota.go generated vendored Normal file
View File

@@ -0,0 +1,76 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// Quota defines a threshold against a number of bytes.
type Quota struct {
Bytes uint64
Consumed uint64
Over bool
}
func (q *Quota) marshal(fam byte) ([]byte, error) {
attrs := []netlink.Attribute{
{Type: unix.NFTA_QUOTA_BYTES, Data: binaryutil.BigEndian.PutUint64(q.Bytes)},
{Type: unix.NFTA_QUOTA_CONSUMED, Data: binaryutil.BigEndian.PutUint64(q.Consumed)},
}
flags := uint32(0)
if q.Over {
flags = unix.NFT_QUOTA_F_INV
}
attrs = append(attrs, netlink.Attribute{
Type: unix.NFTA_QUOTA_FLAGS,
Data: binaryutil.BigEndian.PutUint32(flags),
})
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("quota\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (q *Quota) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_QUOTA_BYTES:
q.Bytes = ad.Uint64()
case unix.NFTA_QUOTA_CONSUMED:
q.Consumed = ad.Uint64()
case unix.NFTA_QUOTA_FLAGS:
q.Over = (ad.Uint32() & unix.NFT_QUOTA_F_INV) == 1
}
}
return ad.Err()
}

124
vendor/github.com/google/nftables/expr/range.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
// Copyright 2019 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// Range implements range expression
type Range struct {
Op CmpOp
Register uint32
FromData []byte
ToData []byte
}
func (e *Range) marshal(fam byte) ([]byte, error) {
var attrs []netlink.Attribute
var err error
var rangeFromData, rangeToData []byte
if e.Register > 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_RANGE_SREG, Data: binaryutil.BigEndian.PutUint32(e.Register)})
}
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_RANGE_OP, Data: binaryutil.BigEndian.PutUint32(uint32(e.Op))})
if len(e.FromData) > 0 {
rangeFromData, err = nestedAttr(e.FromData, unix.NFTA_RANGE_FROM_DATA)
if err != nil {
return nil, err
}
}
if len(e.ToData) > 0 {
rangeToData, err = nestedAttr(e.ToData, unix.NFTA_RANGE_TO_DATA)
if err != nil {
return nil, err
}
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
data = append(data, rangeFromData...)
data = append(data, rangeToData...)
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("range\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Range) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_RANGE_OP:
e.Op = CmpOp(ad.Uint32())
case unix.NFTA_RANGE_SREG:
e.Register = ad.Uint32()
case unix.NFTA_RANGE_FROM_DATA:
ad.Do(func(b []byte) error {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
if ad.Next() && ad.Type() == unix.NFTA_DATA_VALUE {
ad.Do(func(b []byte) error {
e.FromData = b
return nil
})
}
return ad.Err()
})
case unix.NFTA_RANGE_TO_DATA:
ad.Do(func(b []byte) error {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
if ad.Next() && ad.Type() == unix.NFTA_DATA_VALUE {
ad.Do(func(b []byte) error {
e.ToData = b
return nil
})
}
return ad.Err()
})
}
}
return ad.Err()
}
func nestedAttr(data []byte, attrType uint16) ([]byte, error) {
ae := netlink.NewAttributeEncoder()
ae.Do(unix.NLA_F_NESTED|attrType, func() ([]byte, error) {
nae := netlink.NewAttributeEncoder()
nae.ByteOrder = binary.BigEndian
nae.Bytes(unix.NFTA_DATA_VALUE, data)
return nae.Encode()
})
return ae.Encode()
}

71
vendor/github.com/google/nftables/expr/redirect.go generated vendored Normal file
View File

@@ -0,0 +1,71 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Redir struct {
RegisterProtoMin uint32
RegisterProtoMax uint32
Flags uint32
}
func (e *Redir) marshal(fam byte) ([]byte, error) {
var attrs []netlink.Attribute
if e.RegisterProtoMin > 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_REDIR_REG_PROTO_MIN, Data: binaryutil.BigEndian.PutUint32(e.RegisterProtoMin)})
}
if e.RegisterProtoMax > 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_REDIR_REG_PROTO_MAX, Data: binaryutil.BigEndian.PutUint32(e.RegisterProtoMax)})
}
if e.Flags > 0 {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_REDIR_FLAGS, Data: binaryutil.BigEndian.PutUint32(e.Flags)})
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("redir\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Redir) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_REDIR_REG_PROTO_MIN:
e.RegisterProtoMin = ad.Uint32()
case unix.NFTA_REDIR_REG_PROTO_MAX:
e.RegisterProtoMax = ad.Uint32()
case unix.NFTA_REDIR_FLAGS:
e.Flags = ad.Uint32()
}
}
return ad.Err()
}

59
vendor/github.com/google/nftables/expr/reject.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Reject struct {
Type uint32
Code uint8
}
func (e *Reject) marshal(fam byte) ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_REJECT_TYPE, Data: binaryutil.BigEndian.PutUint32(e.Type)},
{Type: unix.NFTA_REJECT_ICMP_CODE, Data: []byte{e.Code}},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("reject\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Reject) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_REJECT_TYPE:
e.Type = ad.Uint32()
case unix.NFTA_REJECT_ICMP_CODE:
e.Code = ad.Uint8()
}
}
return ad.Err()
}

55
vendor/github.com/google/nftables/expr/rt.go generated vendored Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type RtKey uint32
const (
RtClassid RtKey = unix.NFT_RT_CLASSID
RtNexthop4 RtKey = unix.NFT_RT_NEXTHOP4
RtNexthop6 RtKey = unix.NFT_RT_NEXTHOP6
RtTCPMSS RtKey = unix.NFT_RT_TCPMSS
)
type Rt struct {
Register uint32
Key RtKey
}
func (e *Rt) marshal(fam byte) ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_RT_KEY, Data: binaryutil.BigEndian.PutUint32(uint32(e.Key))},
{Type: unix.NFTA_RT_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("rt\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Rt) unmarshal(fam byte, data []byte) error {
return fmt.Errorf("not yet implemented")
}

89
vendor/github.com/google/nftables/expr/socket.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
// Copyright 2023 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"golang.org/x/sys/unix"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
)
type Socket struct {
Key SocketKey
Level uint32
Register uint32
}
type SocketKey uint32
const (
// TODO, Once the constants below are available in golang.org/x/sys/unix, switch to use those.
NFTA_SOCKET_KEY = 1
NFTA_SOCKET_DREG = 2
NFTA_SOCKET_LEVEL = 3
NFT_SOCKET_TRANSPARENT = 0
NFT_SOCKET_MARK = 1
NFT_SOCKET_WILDCARD = 2
NFT_SOCKET_CGROUPV2 = 3
SocketKeyTransparent SocketKey = NFT_SOCKET_TRANSPARENT
SocketKeyMark SocketKey = NFT_SOCKET_MARK
SocketKeyWildcard SocketKey = NFT_SOCKET_WILDCARD
SocketKeyCgroupv2 SocketKey = NFT_SOCKET_CGROUPV2
)
func (e *Socket) marshal(fam byte) ([]byte, error) {
// NOTE: Socket.Level is only used when Socket.Key == SocketKeyCgroupv2. But `nft` always encoding it. Check link below:
// http://git.netfilter.org/nftables/tree/src/netlink_linearize.c?id=0583bac241ea18c9d7f61cb20ca04faa1e043b78#n319
exprData, err := netlink.MarshalAttributes(
[]netlink.Attribute{
{Type: NFTA_SOCKET_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
{Type: NFTA_SOCKET_KEY, Data: binaryutil.BigEndian.PutUint32(uint32(e.Key))},
{Type: NFTA_SOCKET_LEVEL, Data: binaryutil.BigEndian.PutUint32(uint32(e.Level))},
},
)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("socket\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: exprData},
})
}
func (e *Socket) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case NFTA_SOCKET_DREG:
e.Register = ad.Uint32()
case NFTA_SOCKET_KEY:
e.Key = SocketKey(ad.Uint32())
case NFTA_SOCKET_LEVEL:
e.Level = ad.Uint32()
}
}
return ad.Err()
}

79
vendor/github.com/google/nftables/expr/target.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
package expr
import (
"bytes"
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/xt"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// See https://git.netfilter.org/libnftnl/tree/src/expr/target.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n28
const XTablesExtensionNameMaxLen = 29
// See https://git.netfilter.org/libnftnl/tree/src/expr/target.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n30
type Target struct {
Name string
Rev uint32
Info xt.InfoAny
}
func (e *Target) marshal(fam byte) ([]byte, error) {
// Per https://git.netfilter.org/libnftnl/tree/src/expr/target.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n38
name := e.Name
// limit the extension name as (some) user-space tools do and leave room for
// trailing \x00
if len(name) >= /* sic! */ XTablesExtensionNameMaxLen {
name = name[:XTablesExtensionNameMaxLen-1] // leave room for trailing \x00.
}
// Marshalling assumes that the correct Info type for the particular table
// family and Match revision has been set.
info, err := xt.Marshal(xt.TableFamily(fam), e.Rev, e.Info)
if err != nil {
return nil, err
}
attrs := []netlink.Attribute{
{Type: unix.NFTA_TARGET_NAME, Data: []byte(name + "\x00")},
{Type: unix.NFTA_TARGET_REV, Data: binaryutil.BigEndian.PutUint32(e.Rev)},
{Type: unix.NFTA_TARGET_INFO, Data: info},
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("target\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Target) unmarshal(fam byte, data []byte) error {
// Per https://git.netfilter.org/libnftnl/tree/src/expr/target.c?id=09456c720e9c00eecc08e41ac6b7c291b3821ee5#n65
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
var info []byte
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_TARGET_NAME:
// We are forgiving here, accepting any length and even missing terminating \x00.
e.Name = string(bytes.TrimRight(ad.Bytes(), "\x00"))
case unix.NFTA_TARGET_REV:
e.Rev = ad.Uint32()
case unix.NFTA_TARGET_INFO:
info = ad.Bytes()
}
}
if err = ad.Err(); err != nil {
return err
}
e.Info, err = xt.Unmarshal(e.Name, xt.TableFamily(fam), e.Rev, info)
return err
}

82
vendor/github.com/google/nftables/expr/tproxy.go generated vendored Normal file
View File

@@ -0,0 +1,82 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
const (
// NFTA_TPROXY_FAMILY defines attribute for a table family
NFTA_TPROXY_FAMILY = 0x01
// NFTA_TPROXY_REG_ADDR defines attribute for a register carrying redirection address value
NFTA_TPROXY_REG_ADDR = 0x02
// NFTA_TPROXY_REG_PORT defines attribute for a register carrying redirection port value
NFTA_TPROXY_REG_PORT = 0x03
)
// TProxy defines struct with parameters for the transparent proxy
type TProxy struct {
Family byte
TableFamily byte
RegAddr uint32
RegPort uint32
}
func (e *TProxy) marshal(fam byte) ([]byte, error) {
attrs := []netlink.Attribute{
{Type: NFTA_TPROXY_FAMILY, Data: binaryutil.BigEndian.PutUint32(uint32(e.Family))},
{Type: NFTA_TPROXY_REG_PORT, Data: binaryutil.BigEndian.PutUint32(e.RegPort)},
}
if e.RegAddr != 0 {
attrs = append(attrs, netlink.Attribute{
Type: NFTA_TPROXY_REG_ADDR,
Data: binaryutil.BigEndian.PutUint32(e.RegAddr),
})
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("tproxy\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *TProxy) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case NFTA_TPROXY_FAMILY:
e.Family = ad.Uint8()
case NFTA_TPROXY_REG_PORT:
e.RegPort = ad.Uint32()
case NFTA_TPROXY_REG_ADDR:
e.RegAddr = ad.Uint32()
}
}
return ad.Err()
}

128
vendor/github.com/google/nftables/expr/verdict.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// This code assembles the verdict structure, as expected by the
// nftables netlink API.
// For further information, consult:
// - netfilter.h (Linux kernel)
// - net/netfilter/nf_tables_api.c (Linux kernel)
// - src/expr/data_reg.c (linbnftnl)
type Verdict struct {
Kind VerdictKind
Chain string
}
type VerdictKind int64
// Verdicts, as per netfilter.h and netfilter/nf_tables.h.
const (
VerdictReturn VerdictKind = iota - 5
VerdictGoto
VerdictJump
VerdictBreak
VerdictContinue
VerdictDrop
VerdictAccept
VerdictStolen
VerdictQueue
VerdictRepeat
VerdictStop
)
func (e *Verdict) marshal(fam byte) ([]byte, error) {
// A verdict is a tree of netlink attributes structured as follows:
// NFTA_LIST_ELEM | NLA_F_NESTED {
// NFTA_EXPR_NAME { "immediate\x00" }
// NFTA_EXPR_DATA | NLA_F_NESTED {
// NFTA_IMMEDIATE_DREG { NFT_REG_VERDICT }
// NFTA_IMMEDIATE_DATA | NLA_F_NESTED {
// the verdict code
// }
// }
// }
attrs := []netlink.Attribute{
{Type: unix.NFTA_VERDICT_CODE, Data: binaryutil.BigEndian.PutUint32(uint32(e.Kind))},
}
if e.Chain != "" {
attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_VERDICT_CHAIN, Data: []byte(e.Chain + "\x00")})
}
codeData, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
immData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NLA_F_NESTED | unix.NFTA_DATA_VERDICT, Data: codeData},
})
if err != nil {
return nil, err
}
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_IMMEDIATE_DREG, Data: binaryutil.BigEndian.PutUint32(unix.NFT_REG_VERDICT)},
{Type: unix.NLA_F_NESTED | unix.NFTA_IMMEDIATE_DATA, Data: immData},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("immediate\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}
func (e *Verdict) unmarshal(fam byte, data []byte) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_IMMEDIATE_DATA:
nestedAD, err := netlink.NewAttributeDecoder(ad.Bytes())
if err != nil {
return fmt.Errorf("nested NewAttributeDecoder() failed: %v", err)
}
for nestedAD.Next() {
switch nestedAD.Type() {
case unix.NFTA_DATA_VERDICT:
e.Kind = VerdictKind(int32(binaryutil.BigEndian.Uint32(nestedAD.Bytes()[4:8])))
if len(nestedAD.Bytes()) > 12 {
e.Chain = string(bytes.Trim(nestedAD.Bytes()[12:], "\x00"))
}
}
}
if nestedAD.Err() != nil {
return fmt.Errorf("decoding immediate: %v", nestedAD.Err())
}
}
}
return ad.Err()
}

306
vendor/github.com/google/nftables/flowtable.go generated vendored Normal file
View File

@@ -0,0 +1,306 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"encoding/binary"
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
const (
// not in ztypes_linux.go, added here
// https://cs.opensource.google/go/x/sys/+/c6bc011c:unix/ztypes_linux.go;l=1870-1892
NFT_MSG_NEWFLOWTABLE = 0x16
NFT_MSG_GETFLOWTABLE = 0x17
NFT_MSG_DELFLOWTABLE = 0x18
)
const (
// not in ztypes_linux.go, added here
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n1634
_ = iota
NFTA_FLOWTABLE_TABLE
NFTA_FLOWTABLE_NAME
NFTA_FLOWTABLE_HOOK
NFTA_FLOWTABLE_USE
NFTA_FLOWTABLE_HANDLE
NFTA_FLOWTABLE_PAD
NFTA_FLOWTABLE_FLAGS
)
const (
// not in ztypes_linux.go, added here
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n1657
_ = iota
NFTA_FLOWTABLE_HOOK_NUM
NFTA_FLOWTABLE_PRIORITY
NFTA_FLOWTABLE_DEVS
)
const (
// not in ztypes_linux.go, added here, used for flowtable device name specification
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n1709
NFTA_DEVICE_NAME = 1
)
type FlowtableFlags uint32
const (
_ FlowtableFlags = iota
FlowtableFlagsHWOffload
FlowtableFlagsCounter
FlowtableFlagsMask = (FlowtableFlagsHWOffload | FlowtableFlagsCounter)
)
type FlowtableHook uint32
func FlowtableHookRef(h FlowtableHook) *FlowtableHook {
return &h
}
var (
// Only ingress is supported
// https://github.com/torvalds/linux/blob/b72018ab8236c3ae427068adeb94bdd3f20454ec/net/netfilter/nf_tables_api.c#L7378-L7379
FlowtableHookIngress *FlowtableHook = FlowtableHookRef(unix.NF_NETDEV_INGRESS)
)
type FlowtablePriority int32
func FlowtablePriorityRef(p FlowtablePriority) *FlowtablePriority {
return &p
}
var (
// As per man page:
// The priority can be a signed integer or filter which stands for 0. Addition and subtraction can be used to set relative priority, e.g. filter + 5 equals to 5.
// https://git.netfilter.org/nftables/tree/doc/nft.txt?id=8c600a843b7c0c1cc275ecc0603bd1fc57773e98#n712
FlowtablePriorityFilter *FlowtablePriority = FlowtablePriorityRef(0)
)
type Flowtable struct {
Table *Table
Name string
Hooknum *FlowtableHook
Priority *FlowtablePriority
Devices []string
Use uint32
// Bitmask flags, can be HW_OFFLOAD or COUNTER
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n1621
Flags FlowtableFlags
Handle uint64
}
func (cc *Conn) AddFlowtable(f *Flowtable) *Flowtable {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: NFTA_FLOWTABLE_TABLE, Data: []byte(f.Table.Name)},
{Type: NFTA_FLOWTABLE_NAME, Data: []byte(f.Name)},
{Type: NFTA_FLOWTABLE_FLAGS, Data: binaryutil.BigEndian.PutUint32(uint32(f.Flags))},
})
if f.Hooknum == nil {
f.Hooknum = FlowtableHookIngress
}
if f.Priority == nil {
f.Priority = FlowtablePriorityFilter
}
hookAttr := []netlink.Attribute{
{Type: NFTA_FLOWTABLE_HOOK_NUM, Data: binaryutil.BigEndian.PutUint32(uint32(*f.Hooknum))},
{Type: NFTA_FLOWTABLE_PRIORITY, Data: binaryutil.BigEndian.PutUint32(uint32(*f.Priority))},
}
if len(f.Devices) > 0 {
devs := make([]netlink.Attribute, len(f.Devices))
for i, d := range f.Devices {
devs[i] = netlink.Attribute{Type: NFTA_DEVICE_NAME, Data: []byte(d)}
}
hookAttr = append(hookAttr, netlink.Attribute{
Type: unix.NLA_F_NESTED | NFTA_FLOWTABLE_DEVS,
Data: cc.marshalAttr(devs),
})
}
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NLA_F_NESTED | NFTA_FLOWTABLE_HOOK, Data: cc.marshalAttr(hookAttr)},
})...)
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWFLOWTABLE),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: append(extraHeader(uint8(f.Table.Family), 0), data...),
})
return f
}
func (cc *Conn) DelFlowtable(f *Flowtable) {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: NFTA_FLOWTABLE_TABLE, Data: []byte(f.Table.Name)},
{Type: NFTA_FLOWTABLE_NAME, Data: []byte(f.Name)},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_DELFLOWTABLE),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(f.Table.Family), 0), data...),
})
}
func (cc *Conn) ListFlowtables(t *Table) ([]*Flowtable, error) {
reply, err := cc.getFlowtables(t)
if err != nil {
return nil, err
}
var fts []*Flowtable
for _, msg := range reply {
f, err := ftsFromMsg(msg)
if err != nil {
return nil, err
}
f.Table = t
fts = append(fts, f)
}
return fts, nil
}
func (cc *Conn) getFlowtables(t *Table) ([]netlink.Message, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
attrs := []netlink.Attribute{
{Type: NFTA_FLOWTABLE_TABLE, Data: []byte(t.Name + "\x00")},
}
data, err := netlink.MarshalAttributes(attrs)
if err != nil {
return nil, err
}
message := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_GETFLOWTABLE),
Flags: netlink.Request | netlink.Acknowledge | netlink.Dump,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
}
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
return nil, fmt.Errorf("SendMessages: %v", err)
}
reply, err := receiveAckAware(conn, message.Header.Flags)
if err != nil {
return nil, fmt.Errorf("Receive: %v", err)
}
return reply, nil
}
func ftsFromMsg(msg netlink.Message) (*Flowtable, error) {
flowHeaderType := netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWFLOWTABLE)
if got, want := msg.Header.Type, flowHeaderType; got != want {
return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
var ft Flowtable
for ad.Next() {
switch ad.Type() {
case NFTA_FLOWTABLE_NAME:
ft.Name = ad.String()
case NFTA_FLOWTABLE_USE:
ft.Use = ad.Uint32()
case NFTA_FLOWTABLE_HANDLE:
ft.Handle = ad.Uint64()
case NFTA_FLOWTABLE_FLAGS:
ft.Flags = FlowtableFlags(ad.Uint32())
case NFTA_FLOWTABLE_HOOK:
ad.Do(func(b []byte) error {
ft.Hooknum, ft.Priority, ft.Devices, err = ftsHookFromMsg(b)
return err
})
}
}
return &ft, nil
}
func ftsHookFromMsg(b []byte) (*FlowtableHook, *FlowtablePriority, []string, error) {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return nil, nil, nil, err
}
ad.ByteOrder = binary.BigEndian
var hooknum FlowtableHook
var prio FlowtablePriority
var devices []string
for ad.Next() {
switch ad.Type() {
case NFTA_FLOWTABLE_HOOK_NUM:
hooknum = FlowtableHook(ad.Uint32())
case NFTA_FLOWTABLE_PRIORITY:
prio = FlowtablePriority(ad.Uint32())
case NFTA_FLOWTABLE_DEVS:
ad.Do(func(b []byte) error {
devices, err = devsFromMsg(b)
return err
})
}
}
return &hooknum, &prio, devices, nil
}
func devsFromMsg(b []byte) ([]string, error) {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
devs := make([]string, 0)
for ad.Next() {
switch ad.Type() {
case NFTA_DEVICE_NAME:
devs = append(devs, ad.String())
}
}
return devs, nil
}

View File

@@ -0,0 +1,10 @@
package parseexprfunc
import (
"github.com/mdlayher/netlink"
)
var (
ParseExprBytesFunc func(fam byte, ad *netlink.AttributeDecoder, b []byte) ([]interface{}, error)
ParseExprMsgFunc func(fam byte, b []byte) ([]interface{}, error)
)

319
vendor/github.com/google/nftables/monitor.go generated vendored Normal file
View File

@@ -0,0 +1,319 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"math"
"strings"
"sync"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type MonitorAction uint8
// Possible MonitorAction values.
const (
MonitorActionNew MonitorAction = 1 << iota
MonitorActionDel
MonitorActionMask MonitorAction = (1 << iota) - 1
MonitorActionAny MonitorAction = MonitorActionMask
)
type MonitorObject uint32
// Possible MonitorObject values.
const (
MonitorObjectTables MonitorObject = 1 << iota
MonitorObjectChains
MonitorObjectSets
MonitorObjectRules
MonitorObjectElements
MonitorObjectRuleset
MonitorObjectMask MonitorObject = (1 << iota) - 1
MonitorObjectAny MonitorObject = MonitorObjectMask
)
var (
monitorFlags = map[MonitorAction]map[MonitorObject]uint32{
MonitorActionAny: {
MonitorObjectAny: 0xffffffff,
MonitorObjectTables: 1<<unix.NFT_MSG_NEWTABLE | 1<<unix.NFT_MSG_DELTABLE,
MonitorObjectChains: 1<<unix.NFT_MSG_NEWCHAIN | 1<<unix.NFT_MSG_DELCHAIN,
MonitorObjectRules: 1<<unix.NFT_MSG_NEWRULE | 1<<unix.NFT_MSG_DELRULE,
MonitorObjectSets: 1<<unix.NFT_MSG_NEWSET | 1<<unix.NFT_MSG_DELSET,
MonitorObjectElements: 1<<unix.NFT_MSG_NEWSETELEM | 1<<unix.NFT_MSG_DELSETELEM,
MonitorObjectRuleset: 1<<unix.NFT_MSG_NEWTABLE | 1<<unix.NFT_MSG_DELTABLE |
1<<unix.NFT_MSG_NEWCHAIN | 1<<unix.NFT_MSG_DELCHAIN |
1<<unix.NFT_MSG_NEWRULE | 1<<unix.NFT_MSG_DELRULE |
1<<unix.NFT_MSG_NEWSET | 1<<unix.NFT_MSG_DELSET |
1<<unix.NFT_MSG_NEWSETELEM | 1<<unix.NFT_MSG_DELSETELEM |
1<<unix.NFT_MSG_NEWOBJ | 1<<unix.NFT_MSG_DELOBJ,
},
MonitorActionNew: {
MonitorObjectAny: 1<<unix.NFT_MSG_NEWTABLE |
1<<unix.NFT_MSG_NEWCHAIN |
1<<unix.NFT_MSG_NEWRULE |
1<<unix.NFT_MSG_NEWSET |
1<<unix.NFT_MSG_NEWSETELEM,
MonitorObjectTables: 1 << unix.NFT_MSG_NEWTABLE,
MonitorObjectChains: 1 << unix.NFT_MSG_NEWCHAIN,
MonitorObjectRules: 1 << unix.NFT_MSG_NEWRULE,
MonitorObjectSets: 1 << unix.NFT_MSG_NEWSET,
MonitorObjectRuleset: 1<<unix.NFT_MSG_NEWTABLE |
1<<unix.NFT_MSG_NEWCHAIN |
1<<unix.NFT_MSG_NEWRULE |
1<<unix.NFT_MSG_NEWSET |
1<<unix.NFT_MSG_NEWSETELEM |
1<<unix.NFT_MSG_NEWOBJ,
},
MonitorActionDel: {
MonitorObjectAny: 1<<unix.NFT_MSG_DELTABLE |
1<<unix.NFT_MSG_DELCHAIN |
1<<unix.NFT_MSG_DELRULE |
1<<unix.NFT_MSG_DELSET |
1<<unix.NFT_MSG_DELSETELEM |
1<<unix.NFT_MSG_DELOBJ,
},
}
)
type MonitorEventType int
const (
MonitorEventTypeNewTable MonitorEventType = unix.NFT_MSG_NEWTABLE
MonitorEventTypeDelTable MonitorEventType = unix.NFT_MSG_DELTABLE
MonitorEventTypeNewChain MonitorEventType = unix.NFT_MSG_NEWCHAIN
MonitorEventTypeDelChain MonitorEventType = unix.NFT_MSG_DELCHAIN
MonitorEventTypeNewRule MonitorEventType = unix.NFT_MSG_NEWRULE
MonitorEventTypeDelRule MonitorEventType = unix.NFT_MSG_DELRULE
MonitorEventTypeNewSet MonitorEventType = unix.NFT_MSG_NEWSET
MonitorEventTypeDelSet MonitorEventType = unix.NFT_MSG_DELSET
MonitorEventTypeNewSetElem MonitorEventType = unix.NFT_MSG_NEWSETELEM
MonitorEventTypeDelSetElem MonitorEventType = unix.NFT_MSG_DELSETELEM
MonitorEventTypeNewObj MonitorEventType = unix.NFT_MSG_NEWOBJ
MonitorEventTypeDelObj MonitorEventType = unix.NFT_MSG_DELOBJ
MonitorEventTypeOOB MonitorEventType = math.MaxInt // out of band event
)
// A MonitorEvent represents a single change received via a [Monitor].
//
// Depending on the Type, the Data field can be type-asserted to the specific
// data type for this event, e.g. when Type is
// nftables.MonitorEventTypeNewTable, you can access the corresponding table
// details via Data.(*nftables.Table).
type MonitorEvent struct {
Type MonitorEventType
Data any
Error error
}
const (
monitorOK = iota
monitorClosed
)
// A Monitor is an event-based nftables monitor that will receive one event per
// new (or deleted) table, chain, rule, set, etc., depending on the monitor
// configuration.
type Monitor struct {
action MonitorAction
object MonitorObject
monitorFlags uint32
conn *netlink.Conn
closer netlinkCloser
// mu covers eventCh and status
mu sync.Mutex
eventCh chan *MonitorEvent
status int
}
type MonitorOption func(*Monitor)
func WithMonitorEventBuffer(size int) MonitorOption {
return func(monitor *Monitor) {
monitor.eventCh = make(chan *MonitorEvent, size)
}
}
// WithMonitorAction to set monitor actions like new, del or any.
func WithMonitorAction(action MonitorAction) MonitorOption {
return func(monitor *Monitor) {
monitor.action = action
}
}
// WithMonitorObject to set monitor objects.
func WithMonitorObject(object MonitorObject) MonitorOption {
return func(monitor *Monitor) {
monitor.object = object
}
}
// NewMonitor returns a Monitor with options to be started.
//
// Note that NewMonitor only prepares a Monitor. To install the monitor, call
// [Conn.AddMonitor].
func NewMonitor(opts ...MonitorOption) *Monitor {
monitor := &Monitor{
status: monitorOK,
}
for _, opt := range opts {
opt(monitor)
}
if monitor.eventCh == nil {
monitor.eventCh = make(chan *MonitorEvent)
}
objects, ok := monitorFlags[monitor.action]
if !ok {
objects = monitorFlags[MonitorActionAny]
}
flags, ok := objects[monitor.object]
if !ok {
flags = objects[MonitorObjectAny]
}
monitor.monitorFlags = flags
return monitor
}
func (monitor *Monitor) monitor() {
for {
msgs, err := monitor.conn.Receive()
if err != nil {
if strings.Contains(err.Error(), "use of closed file") {
// ignore the error that be closed
break
} else {
// any other errors will be send to user, and then to close eventCh
event := &MonitorEvent{
Type: MonitorEventTypeOOB,
Data: nil,
Error: err,
}
monitor.eventCh <- event
break
}
}
for _, msg := range msgs {
if msg.Header.Type&0xff00>>8 != netlink.HeaderType(unix.NFNL_SUBSYS_NFTABLES) {
continue
}
msgType := msg.Header.Type & 0x00ff
if monitor.monitorFlags&1<<msgType == 0 {
continue
}
switch msgType {
case unix.NFT_MSG_NEWTABLE, unix.NFT_MSG_DELTABLE:
table, err := tableFromMsg(msg)
event := &MonitorEvent{
Type: MonitorEventType(msgType),
Data: table,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWCHAIN, unix.NFT_MSG_DELCHAIN:
chain, err := chainFromMsg(msg)
event := &MonitorEvent{
Type: MonitorEventType(msgType),
Data: chain,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWRULE, unix.NFT_MSG_DELRULE:
rule, err := parseRuleFromMsg(msg)
event := &MonitorEvent{
Type: MonitorEventType(msgType),
Data: rule,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWSET, unix.NFT_MSG_DELSET:
set, err := setsFromMsg(msg)
event := &MonitorEvent{
Type: MonitorEventType(msgType),
Data: set,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWSETELEM, unix.NFT_MSG_DELSETELEM:
elems, err := elementsFromMsg(uint8(TableFamilyUnspecified), msg)
event := &MonitorEvent{
Type: MonitorEventType(msgType),
Data: elems,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWOBJ, unix.NFT_MSG_DELOBJ:
obj, err := objFromMsg(msg)
event := &MonitorEvent{
Type: MonitorEventType(msgType),
Data: obj,
Error: err,
}
monitor.eventCh <- event
}
}
}
monitor.mu.Lock()
defer monitor.mu.Unlock()
if monitor.status != monitorClosed {
monitor.status = monitorClosed
}
close(monitor.eventCh)
}
func (monitor *Monitor) Close() error {
monitor.mu.Lock()
defer monitor.mu.Unlock()
if monitor.status != monitorClosed {
monitor.status = monitorClosed
return monitor.closer()
}
return nil
}
// AddMonitor to perform the monitor immediately. The channel will be closed after
// calling Close on Monitor or encountering a netlink conn error while Receive.
// Caller may receive a MonitorEventTypeOOB event which contains an error we didn't
// handle, for now.
func (cc *Conn) AddMonitor(monitor *Monitor) (chan *MonitorEvent, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
monitor.conn = conn
monitor.closer = closer
if monitor.monitorFlags != 0 {
if err = conn.JoinGroup(uint32(unix.NFNLGRP_NFTABLES)); err != nil {
monitor.closer()
return nil, err
}
}
go monitor.monitor()
return monitor.eventCh, nil
}
func parseRuleFromMsg(msg netlink.Message) (*Rule, error) {
genmsg := &NFGenMsg{}
genmsg.Decode(msg.Data[:4])
return ruleFromMsg(TableFamily(genmsg.NFGenFamily), msg)
}

241
vendor/github.com/google/nftables/obj.go generated vendored Normal file
View File

@@ -0,0 +1,241 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"encoding/binary"
"fmt"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
var (
newObjHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWOBJ)
delObjHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELOBJ)
)
// Obj represents a netfilter stateful object. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Stateful_objects
type Obj interface {
table() *Table
family() TableFamily
unmarshal(*netlink.AttributeDecoder) error
marshal(data bool) ([]byte, error)
}
// AddObject adds the specified Obj. Alias of AddObj.
func (cc *Conn) AddObject(o Obj) Obj {
return cc.AddObj(o)
}
// AddObj adds the specified Obj. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Stateful_objects
func (cc *Conn) AddObj(o Obj) Obj {
cc.mu.Lock()
defer cc.mu.Unlock()
data, err := o.marshal(true)
if err != nil {
cc.setErr(err)
return nil
}
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWOBJ),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: append(extraHeader(uint8(o.family()), 0), data...),
})
return o
}
// DeleteObject deletes the specified Obj
func (cc *Conn) DeleteObject(o Obj) {
cc.mu.Lock()
defer cc.mu.Unlock()
data, err := o.marshal(false)
if err != nil {
cc.setErr(err)
return
}
data = append(data, cc.marshalAttr([]netlink.Attribute{{Type: unix.NLA_F_NESTED | unix.NFTA_OBJ_DATA}})...)
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELOBJ),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(o.family()), 0), data...),
})
}
// GetObj is a legacy method that return all Obj that belongs
// to the same table as the given one
func (cc *Conn) GetObj(o Obj) ([]Obj, error) {
return cc.getObj(nil, o.table(), unix.NFT_MSG_GETOBJ)
}
// GetObjReset is a legacy method that reset all Obj that belongs
// the same table as the given one
func (cc *Conn) GetObjReset(o Obj) ([]Obj, error) {
return cc.getObj(nil, o.table(), unix.NFT_MSG_GETOBJ_RESET)
}
// GetObject gets the specified Object
func (cc *Conn) GetObject(o Obj) (Obj, error) {
objs, err := cc.getObj(o, o.table(), unix.NFT_MSG_GETOBJ)
if len(objs) == 0 {
return nil, err
}
return objs[0], err
}
// GetObjects get all the Obj that belongs to the given table
func (cc *Conn) GetObjects(t *Table) ([]Obj, error) {
return cc.getObj(nil, t, unix.NFT_MSG_GETOBJ)
}
// ResetObject reset the given Obj
func (cc *Conn) ResetObject(o Obj) (Obj, error) {
objs, err := cc.getObj(o, o.table(), unix.NFT_MSG_GETOBJ_RESET)
if len(objs) == 0 {
return nil, err
}
return objs[0], err
}
// ResetObjects reset all the Obj that belongs to the given table
func (cc *Conn) ResetObjects(t *Table) ([]Obj, error) {
return cc.getObj(nil, t, unix.NFT_MSG_GETOBJ_RESET)
}
func objFromMsg(msg netlink.Message) (Obj, error) {
if got, want1, want2 := msg.Header.Type, newObjHeaderType, delObjHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
var (
table *Table
name string
objectType uint32
)
for ad.Next() {
switch ad.Type() {
case unix.NFTA_OBJ_TABLE:
table = &Table{Name: ad.String(), Family: TableFamily(msg.Data[0])}
case unix.NFTA_OBJ_NAME:
name = ad.String()
case unix.NFTA_OBJ_TYPE:
objectType = ad.Uint32()
case unix.NFTA_OBJ_DATA:
switch objectType {
case unix.NFT_OBJECT_COUNTER:
o := CounterObj{
Table: table,
Name: name,
}
ad.Do(func(b []byte) error {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
return o.unmarshal(ad)
})
return &o, ad.Err()
case NFT_OBJECT_QUOTA:
o := QuotaObj{
Table: table,
Name: name,
}
ad.Do(func(b []byte) error {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return err
}
ad.ByteOrder = binary.BigEndian
return o.unmarshal(ad)
})
return &o, ad.Err()
}
}
}
if err := ad.Err(); err != nil {
return nil, err
}
return nil, fmt.Errorf("malformed stateful object")
}
func (cc *Conn) getObj(o Obj, t *Table, msgType uint16) ([]Obj, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
var data []byte
var flags netlink.HeaderFlags
if o != nil {
data, err = o.marshal(false)
} else {
flags = netlink.Dump
data, err = netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_RULE_TABLE, Data: []byte(t.Name + "\x00")},
})
}
if err != nil {
return nil, err
}
message := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | msgType),
Flags: netlink.Request | netlink.Acknowledge | flags,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
}
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
return nil, fmt.Errorf("SendMessages: %v", err)
}
reply, err := receiveAckAware(conn, message.Header.Flags)
if err != nil {
return nil, fmt.Errorf("Receive: %v", err)
}
var objs []Obj
for _, msg := range reply {
o, err := objFromMsg(msg)
if err != nil {
return nil, err
}
objs = append(objs, o)
}
return objs, nil
}

80
vendor/github.com/google/nftables/quota.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2023 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
const (
NFTA_OBJ_USERDATA = 8
NFT_OBJECT_QUOTA = 2
)
type QuotaObj struct {
Table *Table
Name string
Bytes uint64
Consumed uint64
Over bool
}
func (q *QuotaObj) unmarshal(ad *netlink.AttributeDecoder) error {
for ad.Next() {
switch ad.Type() {
case unix.NFTA_QUOTA_BYTES:
q.Bytes = ad.Uint64()
case unix.NFTA_QUOTA_CONSUMED:
q.Consumed = ad.Uint64()
case unix.NFTA_QUOTA_FLAGS:
q.Over = (ad.Uint32() & unix.NFT_QUOTA_F_INV) == 1
}
}
return nil
}
func (q *QuotaObj) marshal(data bool) ([]byte, error) {
flags := uint32(0)
if q.Over {
flags = unix.NFT_QUOTA_F_INV
}
obj, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_QUOTA_BYTES, Data: binaryutil.BigEndian.PutUint64(q.Bytes)},
{Type: unix.NFTA_QUOTA_CONSUMED, Data: binaryutil.BigEndian.PutUint64(q.Consumed)},
{Type: unix.NFTA_QUOTA_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)},
})
if err != nil {
return nil, err
}
attrs := []netlink.Attribute{
{Type: unix.NFTA_OBJ_TABLE, Data: []byte(q.Table.Name + "\x00")},
{Type: unix.NFTA_OBJ_NAME, Data: []byte(q.Name + "\x00")},
{Type: unix.NFTA_OBJ_TYPE, Data: binaryutil.BigEndian.PutUint32(NFT_OBJECT_QUOTA)},
}
if data {
attrs = append(attrs, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_OBJ_DATA, Data: obj})
}
return netlink.MarshalAttributes(attrs)
}
func (q *QuotaObj) table() *Table {
return q.Table
}
func (q *QuotaObj) family() TableFamily {
return q.Table.Family
}

270
vendor/github.com/google/nftables/rule.go generated vendored Normal file
View File

@@ -0,0 +1,270 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"encoding/binary"
"fmt"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/expr"
"github.com/google/nftables/internal/parseexprfunc"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
var (
newRuleHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWRULE)
delRuleHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELRULE)
)
type ruleOperation uint32
// Possible PayloadOperationType values.
const (
operationAdd ruleOperation = iota
operationInsert
operationReplace
)
// A Rule does something with a packet. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Simple_rule_management
type Rule struct {
Table *Table
Chain *Chain
Position uint64
Handle uint64
// The list of possible flags are specified by nftnl_rule_attr, see
// https://git.netfilter.org/libnftnl/tree/include/libnftnl/rule.h#n21
// Current nftables go implementation supports only
// NFTNL_RULE_POSITION flag for setting rule at position 0
Flags uint32
Exprs []expr.Any
UserData []byte
}
// GetRule returns the rules in the specified table and chain.
//
// Deprecated: use GetRules instead.
func (cc *Conn) GetRule(t *Table, c *Chain) ([]*Rule, error) {
return cc.GetRules(t, c)
}
// GetRules returns the rules in the specified table and chain.
func (cc *Conn) GetRules(t *Table, c *Chain) ([]*Rule, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_RULE_TABLE, Data: []byte(t.Name + "\x00")},
{Type: unix.NFTA_RULE_CHAIN, Data: []byte(c.Name + "\x00")},
})
if err != nil {
return nil, err
}
message := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETRULE),
Flags: netlink.Request | netlink.Acknowledge | netlink.Dump | unix.NLM_F_ECHO,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
}
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
return nil, fmt.Errorf("SendMessages: %v", err)
}
reply, err := receiveAckAware(conn, message.Header.Flags)
if err != nil {
return nil, fmt.Errorf("Receive: %v", err)
}
var rules []*Rule
for _, msg := range reply {
r, err := ruleFromMsg(t.Family, msg)
if err != nil {
return nil, err
}
rules = append(rules, r)
}
return rules, nil
}
// AddRule adds the specified Rule
func (cc *Conn) newRule(r *Rule, op ruleOperation) *Rule {
cc.mu.Lock()
defer cc.mu.Unlock()
exprAttrs := make([]netlink.Attribute, len(r.Exprs))
for idx, expr := range r.Exprs {
exprAttrs[idx] = netlink.Attribute{
Type: unix.NLA_F_NESTED | unix.NFTA_LIST_ELEM,
Data: cc.marshalExpr(byte(r.Table.Family), expr),
}
}
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_TABLE, Data: []byte(r.Table.Name + "\x00")},
{Type: unix.NFTA_RULE_CHAIN, Data: []byte(r.Chain.Name + "\x00")},
})
if r.Handle != 0 {
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_HANDLE, Data: binaryutil.BigEndian.PutUint64(r.Handle)},
})...)
}
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NLA_F_NESTED | unix.NFTA_RULE_EXPRESSIONS, Data: cc.marshalAttr(exprAttrs)},
})...)
if compatPolicy, err := getCompatPolicy(r.Exprs); err != nil {
cc.setErr(err)
} else if compatPolicy != nil {
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NLA_F_NESTED | unix.NFTA_RULE_COMPAT, Data: cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_COMPAT_PROTO, Data: binaryutil.BigEndian.PutUint32(compatPolicy.Proto)},
{Type: unix.NFTA_RULE_COMPAT_FLAGS, Data: binaryutil.BigEndian.PutUint32(compatPolicy.Flag & nft_RULE_COMPAT_F_MASK)},
})},
})...)
}
msgData := []byte{}
msgData = append(msgData, data...)
var flags netlink.HeaderFlags
if r.UserData != nil {
msgData = append(msgData, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_USERDATA, Data: r.UserData},
})...)
}
switch op {
case operationAdd:
flags = netlink.Request | netlink.Acknowledge | netlink.Create | unix.NLM_F_ECHO | unix.NLM_F_APPEND
case operationInsert:
flags = netlink.Request | netlink.Acknowledge | netlink.Create | unix.NLM_F_ECHO
case operationReplace:
flags = netlink.Request | netlink.Acknowledge | netlink.Replace | unix.NLM_F_ECHO | unix.NLM_F_REPLACE
}
if r.Position != 0 || (r.Flags&(1<<unix.NFTA_RULE_POSITION)) != 0 {
msgData = append(msgData, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_POSITION, Data: binaryutil.BigEndian.PutUint64(r.Position)},
})...)
}
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: newRuleHeaderType,
Flags: flags,
},
Data: append(extraHeader(uint8(r.Table.Family), 0), msgData...),
})
return r
}
func (cc *Conn) ReplaceRule(r *Rule) *Rule {
return cc.newRule(r, operationReplace)
}
func (cc *Conn) AddRule(r *Rule) *Rule {
if r.Handle != 0 {
return cc.newRule(r, operationReplace)
}
return cc.newRule(r, operationAdd)
}
func (cc *Conn) InsertRule(r *Rule) *Rule {
if r.Handle != 0 {
return cc.newRule(r, operationReplace)
}
return cc.newRule(r, operationInsert)
}
// DelRule deletes the specified Rule, rule's handle cannot be 0
func (cc *Conn) DelRule(r *Rule) error {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_TABLE, Data: []byte(r.Table.Name + "\x00")},
{Type: unix.NFTA_RULE_CHAIN, Data: []byte(r.Chain.Name + "\x00")},
})
if r.Handle == 0 {
return fmt.Errorf("rule's handle cannot be 0")
}
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_HANDLE, Data: binaryutil.BigEndian.PutUint64(r.Handle)},
})...)
flags := netlink.Request | netlink.Acknowledge
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: delRuleHeaderType,
Flags: flags,
},
Data: append(extraHeader(uint8(r.Table.Family), 0), data...),
})
return nil
}
func ruleFromMsg(fam TableFamily, msg netlink.Message) (*Rule, error) {
if got, want1, want2 := msg.Header.Type, newRuleHeaderType, delRuleHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", msg.Header.Type, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
var r Rule
for ad.Next() {
switch ad.Type() {
case unix.NFTA_RULE_TABLE:
r.Table = &Table{
Name: ad.String(),
Family: fam,
}
case unix.NFTA_RULE_CHAIN:
r.Chain = &Chain{Name: ad.String()}
case unix.NFTA_RULE_EXPRESSIONS:
ad.Do(func(b []byte) error {
exprs, err := parseexprfunc.ParseExprMsgFunc(byte(fam), b)
if err != nil {
return err
}
r.Exprs = make([]expr.Any, len(exprs))
for i := range exprs {
r.Exprs[i] = exprs[i].(expr.Any)
}
return nil
})
case unix.NFTA_RULE_POSITION:
r.Position = ad.Uint64()
case unix.NFTA_RULE_HANDLE:
r.Handle = ad.Uint64()
case unix.NFTA_RULE_USERDATA:
r.UserData = ad.Bytes()
}
}
return &r, ad.Err()
}

937
vendor/github.com/google/nftables/set.go generated vendored Normal file
View File

@@ -0,0 +1,937 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"encoding/binary"
"errors"
"fmt"
"strings"
"time"
"github.com/google/nftables/expr"
"github.com/google/nftables/internal/parseexprfunc"
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
// SetConcatTypeBits defines concatination bits, originally defined in
// https://git.netfilter.org/iptables/tree/iptables/nft.c?id=26753888720d8e7eb422ae4311348347f5a05cb4#n1002
const (
SetConcatTypeBits = 6
SetConcatTypeMask = (1 << SetConcatTypeBits) - 1
// below consts added because not found in go unix package
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n306
NFT_SET_CONCAT = 0x80
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n330
NFTA_SET_DESC_CONCAT = 2
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n428
NFTA_SET_ELEM_KEY_END = 10
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n429
NFTA_SET_ELEM_EXPRESSIONS = 0x11
)
var allocSetID uint32
// SetDatatype represents a datatype declared by nft.
type SetDatatype struct {
Name string
Bytes uint32
// nftMagic represents the magic value that nft uses for
// certain types (ie: IP addresses). We populate SET_KEY_TYPE
// identically, so `nft list ...` commands produce correct output.
nftMagic uint32
}
// GetNFTMagic returns a custom datatype based on user's parameters
func (s *SetDatatype) GetNFTMagic() uint32 {
return s.nftMagic
}
// SetNFTMagic returns a custom datatype based on user's parameters
func (s *SetDatatype) SetNFTMagic(nftMagic uint32) {
s.nftMagic = nftMagic
}
// NFT datatypes. See: https://git.netfilter.org/nftables/tree/include/datatype.h
var (
TypeInvalid = SetDatatype{Name: "invalid", nftMagic: 0}
TypeVerdict = SetDatatype{Name: "verdict", Bytes: 0, nftMagic: 1}
TypeNFProto = SetDatatype{Name: "nf_proto", Bytes: 1, nftMagic: 2}
TypeBitmask = SetDatatype{Name: "bitmask", Bytes: 0, nftMagic: 3}
TypeInteger = SetDatatype{Name: "integer", Bytes: 4, nftMagic: 4}
TypeString = SetDatatype{Name: "string", Bytes: 0, nftMagic: 5}
TypeLLAddr = SetDatatype{Name: "ll_addr", Bytes: 0, nftMagic: 6}
TypeIPAddr = SetDatatype{Name: "ipv4_addr", Bytes: 4, nftMagic: 7}
TypeIP6Addr = SetDatatype{Name: "ipv6_addr", Bytes: 16, nftMagic: 8}
TypeEtherAddr = SetDatatype{Name: "ether_addr", Bytes: 6, nftMagic: 9}
TypeEtherType = SetDatatype{Name: "ether_type", Bytes: 2, nftMagic: 10}
TypeARPOp = SetDatatype{Name: "arp_op", Bytes: 2, nftMagic: 11}
TypeInetProto = SetDatatype{Name: "inet_proto", Bytes: 1, nftMagic: 12}
TypeInetService = SetDatatype{Name: "inet_service", Bytes: 2, nftMagic: 13}
TypeICMPType = SetDatatype{Name: "icmp_type", Bytes: 1, nftMagic: 14}
TypeTCPFlag = SetDatatype{Name: "tcp_flag", Bytes: 1, nftMagic: 15}
TypeDCCPPktType = SetDatatype{Name: "dccp_pkttype", Bytes: 1, nftMagic: 16}
TypeMHType = SetDatatype{Name: "mh_type", Bytes: 1, nftMagic: 17}
TypeTime = SetDatatype{Name: "time", Bytes: 8, nftMagic: 18}
TypeMark = SetDatatype{Name: "mark", Bytes: 4, nftMagic: 19}
TypeIFIndex = SetDatatype{Name: "iface_index", Bytes: 4, nftMagic: 20}
TypeARPHRD = SetDatatype{Name: "iface_type", Bytes: 2, nftMagic: 21}
TypeRealm = SetDatatype{Name: "realm", Bytes: 4, nftMagic: 22}
TypeClassID = SetDatatype{Name: "classid", Bytes: 4, nftMagic: 23}
TypeUID = SetDatatype{Name: "uid", Bytes: sizeOfUIDT, nftMagic: 24}
TypeGID = SetDatatype{Name: "gid", Bytes: sizeOfGIDT, nftMagic: 25}
TypeCTState = SetDatatype{Name: "ct_state", Bytes: 4, nftMagic: 26}
TypeCTDir = SetDatatype{Name: "ct_dir", Bytes: 1, nftMagic: 27}
TypeCTStatus = SetDatatype{Name: "ct_status", Bytes: 4, nftMagic: 28}
TypeICMP6Type = SetDatatype{Name: "icmpv6_type", Bytes: 1, nftMagic: 29}
TypeCTLabel = SetDatatype{Name: "ct_label", Bytes: ctLabelBitSize / 8, nftMagic: 30}
TypePktType = SetDatatype{Name: "pkt_type", Bytes: 1, nftMagic: 31}
TypeICMPCode = SetDatatype{Name: "icmp_code", Bytes: 1, nftMagic: 32}
TypeICMPV6Code = SetDatatype{Name: "icmpv6_code", Bytes: 1, nftMagic: 33}
TypeICMPXCode = SetDatatype{Name: "icmpx_code", Bytes: 1, nftMagic: 34}
TypeDevGroup = SetDatatype{Name: "devgroup", Bytes: 4, nftMagic: 35}
TypeDSCP = SetDatatype{Name: "dscp", Bytes: 1, nftMagic: 36}
TypeECN = SetDatatype{Name: "ecn", Bytes: 1, nftMagic: 37}
TypeFIBAddr = SetDatatype{Name: "fib_addrtype", Bytes: 4, nftMagic: 38}
TypeBoolean = SetDatatype{Name: "boolean", Bytes: 1, nftMagic: 39}
TypeCTEventBit = SetDatatype{Name: "ct_event", Bytes: 4, nftMagic: 40}
TypeIFName = SetDatatype{Name: "ifname", Bytes: ifNameSize, nftMagic: 41}
TypeIGMPType = SetDatatype{Name: "igmp_type", Bytes: 1, nftMagic: 42}
TypeTimeDate = SetDatatype{Name: "time", Bytes: 8, nftMagic: 43}
TypeTimeHour = SetDatatype{Name: "hour", Bytes: 4, nftMagic: 44}
TypeTimeDay = SetDatatype{Name: "day", Bytes: 1, nftMagic: 45}
TypeCGroupV2 = SetDatatype{Name: "cgroupsv2", Bytes: 8, nftMagic: 46}
nftDatatypes = []SetDatatype{
TypeVerdict,
TypeNFProto,
TypeBitmask,
TypeInteger,
TypeString,
TypeLLAddr,
TypeIPAddr,
TypeIP6Addr,
TypeEtherAddr,
TypeEtherType,
TypeARPOp,
TypeInetProto,
TypeInetService,
TypeICMPType,
TypeTCPFlag,
TypeDCCPPktType,
TypeMHType,
TypeTime,
TypeMark,
TypeIFIndex,
TypeARPHRD,
TypeRealm,
TypeClassID,
TypeUID,
TypeGID,
TypeCTState,
TypeCTDir,
TypeCTStatus,
TypeICMP6Type,
TypeCTLabel,
TypePktType,
TypeICMPCode,
TypeICMPV6Code,
TypeICMPXCode,
TypeDevGroup,
TypeDSCP,
TypeECN,
TypeFIBAddr,
TypeBoolean,
TypeCTEventBit,
TypeIFName,
TypeIGMPType,
TypeTimeDate,
TypeTimeHour,
TypeTimeDay,
TypeCGroupV2,
}
// ctLabelBitSize is defined in https://git.netfilter.org/nftables/tree/src/ct.c.
ctLabelBitSize uint32 = 128
// ifNameSize is called IFNAMSIZ in linux/if.h.
ifNameSize uint32 = 16
// bits/typesizes.h
sizeOfUIDT uint32 = 4
sizeOfGIDT uint32 = 4
)
var nftDatatypesByName map[string]SetDatatype
var nftDatatypesByMagic map[uint32]SetDatatype
// Create maps for efficient datatype lookup.
func init() {
nftDatatypesByName = make(map[string]SetDatatype, len(nftDatatypes))
nftDatatypesByMagic = make(map[uint32]SetDatatype, len(nftDatatypes))
for _, dt := range nftDatatypes {
nftDatatypesByName[dt.Name] = dt
nftDatatypesByMagic[dt.nftMagic] = dt
}
}
// ErrTooManyTypes is the error returned by ConcatSetType, if nftMagic would overflow.
var ErrTooManyTypes = errors.New("too many types to concat")
// MustConcatSetType does the same as ConcatSetType, but panics instead of an
// error. It simplifies safe initialization of global variables.
func MustConcatSetType(types ...SetDatatype) SetDatatype {
t, err := ConcatSetType(types...)
if err != nil {
panic(err)
}
return t
}
// ConcatSetType constructs a new SetDatatype which consists of a concatenation
// of the passed types. It returns ErrTooManyTypes, if nftMagic would overflow
// (more than 5 types).
func ConcatSetType(types ...SetDatatype) (SetDatatype, error) {
if len(types) > 32/SetConcatTypeBits {
return SetDatatype{}, ErrTooManyTypes
}
var magic, bytes uint32
names := make([]string, len(types))
for i, t := range types {
bytes += t.Bytes
// concatenated types pad the length to multiples of the register size (4 bytes)
// see https://git.netfilter.org/nftables/tree/src/datatype.c?id=488356b895024d0944b20feb1f930558726e0877#n1162
if t.Bytes%4 != 0 {
bytes += 4 - (t.Bytes % 4)
}
names[i] = t.Name
magic <<= SetConcatTypeBits
magic |= t.nftMagic & SetConcatTypeMask
}
return SetDatatype{Name: strings.Join(names, " . "), Bytes: bytes, nftMagic: magic}, nil
}
// ConcatSetTypeElements uses the ConcatSetType name to calculate and return
// a list of base types which were used to construct the concatenated type
func ConcatSetTypeElements(t SetDatatype) []SetDatatype {
names := strings.Split(t.Name, " . ")
types := make([]SetDatatype, len(names))
for i, n := range names {
types[i] = nftDatatypesByName[n]
}
return types
}
// Set represents an nftables set. Anonymous sets are only valid within the
// context of a single batch.
type Set struct {
Table *Table
ID uint32
Name string
Anonymous bool
Constant bool
Interval bool
IsMap bool
HasTimeout bool
Counter bool
// Can be updated per evaluation path, per `nft list ruleset`
// indicates that set contains "flags dynamic"
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n298
Dynamic bool
// Indicates that the set contains a concatenation
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n306
Concatenation bool
Timeout time.Duration
KeyType SetDatatype
DataType SetDatatype
// Either host (binaryutil.NativeEndian) or big (binaryutil.BigEndian) endian as per
// https://git.netfilter.org/nftables/tree/include/datatype.h?id=d486c9e626405e829221b82d7355558005b26d8a#n109
KeyByteOrder binaryutil.ByteOrder
}
// SetElement represents a data point within a set.
type SetElement struct {
Key []byte
Val []byte
// Field used for definition of ending interval value in concatenated types
// https://git.netfilter.org/libnftnl/tree/include/set_elem.h?id=e2514c0eff4da7e8e0aabd410f7b7d0b7564c880#n11
KeyEnd []byte
IntervalEnd bool
// To support vmap, a caller must be able to pass Verdict type of data.
// If IsMap is true and VerdictData is not nil, then Val of SetElement will be ignored
// and VerdictData will be wrapped into Attribute data.
VerdictData *expr.Verdict
// To support aging of set elements
Timeout time.Duration
// Life left of the "timeout" elements
Expires time.Duration
Counter *expr.Counter
}
func (s *SetElement) decode(fam byte) func(b []byte) error {
return func(b []byte) error {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return fmt.Errorf("failed to create nested attribute decoder: %v", err)
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
switch ad.Type() {
case unix.NFTA_SET_ELEM_KEY:
s.Key, err = decodeElement(ad.Bytes())
if err != nil {
return err
}
case NFTA_SET_ELEM_KEY_END:
s.KeyEnd, err = decodeElement(ad.Bytes())
if err != nil {
return err
}
case unix.NFTA_SET_ELEM_DATA:
s.Val, err = decodeElement(ad.Bytes())
if err != nil {
return err
}
case unix.NFTA_SET_ELEM_FLAGS:
flags := ad.Uint32()
s.IntervalEnd = (flags & unix.NFT_SET_ELEM_INTERVAL_END) != 0
case unix.NFTA_SET_ELEM_TIMEOUT:
s.Timeout = time.Millisecond * time.Duration(ad.Uint64())
case unix.NFTA_SET_ELEM_EXPIRATION:
s.Expires = time.Millisecond * time.Duration(ad.Uint64())
case unix.NFTA_SET_ELEM_EXPR:
elems, err := parseexprfunc.ParseExprBytesFunc(fam, ad, ad.Bytes())
if err != nil {
return err
}
for _, elem := range elems {
switch item := elem.(type) {
case *expr.Counter:
s.Counter = item
}
}
}
}
return ad.Err()
}
}
func decodeElement(d []byte) ([]byte, error) {
ad, err := netlink.NewAttributeDecoder(d)
if err != nil {
return nil, fmt.Errorf("failed to create nested attribute decoder: %v", err)
}
ad.ByteOrder = binary.BigEndian
var b []byte
for ad.Next() {
switch ad.Type() {
case unix.NFTA_SET_ELEM_KEY:
fallthrough
case unix.NFTA_SET_ELEM_DATA:
b = ad.Bytes()
}
}
if err := ad.Err(); err != nil {
return nil, err
}
return b, nil
}
// SetAddElements applies data points to an nftables set.
func (cc *Conn) SetAddElements(s *Set, vals []SetElement) error {
cc.mu.Lock()
defer cc.mu.Unlock()
if s.Anonymous {
return errors.New("anonymous sets cannot be updated")
}
elements, err := s.makeElemList(vals, s.ID)
if err != nil {
return err
}
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSETELEM),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: append(extraHeader(uint8(s.Table.Family), 0), cc.marshalAttr(elements)...),
})
return nil
}
func (s *Set) makeElemList(vals []SetElement, id uint32) ([]netlink.Attribute, error) {
var elements []netlink.Attribute
for i, v := range vals {
item := make([]netlink.Attribute, 0)
var flags uint32
if v.IntervalEnd {
flags |= unix.NFT_SET_ELEM_INTERVAL_END
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_FLAGS | unix.NLA_F_NESTED, Data: binaryutil.BigEndian.PutUint32(flags)})
}
encodedKey, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NFTA_DATA_VALUE, Data: v.Key}})
if err != nil {
return nil, fmt.Errorf("marshal key %d: %v", i, err)
}
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_KEY | unix.NLA_F_NESTED, Data: encodedKey})
if len(v.KeyEnd) > 0 {
encodedKeyEnd, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NFTA_DATA_VALUE, Data: v.KeyEnd}})
if err != nil {
return nil, fmt.Errorf("marshal key end %d: %v", i, err)
}
item = append(item, netlink.Attribute{Type: NFTA_SET_ELEM_KEY_END | unix.NLA_F_NESTED, Data: encodedKeyEnd})
}
if s.HasTimeout && v.Timeout != 0 {
// Set has Timeout flag set, which means an individual element can specify its own timeout.
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_TIMEOUT, Data: binaryutil.BigEndian.PutUint64(uint64(v.Timeout.Milliseconds()))})
}
// The following switch statement deal with 3 different types of elements.
// 1. v is an element of vmap
// 2. v is an element of a regular map
// 3. v is an element of a regular set (default)
switch {
case v.VerdictData != nil:
// Since VerdictData is not nil, v is vmap element, need to add to the attributes
encodedVal := []byte{}
encodedKind, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: binaryutil.BigEndian.PutUint32(uint32(v.VerdictData.Kind))},
})
if err != nil {
return nil, fmt.Errorf("marshal item %d: %v", i, err)
}
encodedVal = append(encodedVal, encodedKind...)
if len(v.VerdictData.Chain) != 0 {
encodedChain, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_SET_ELEM_DATA, Data: []byte(v.VerdictData.Chain + "\x00")},
})
if err != nil {
return nil, fmt.Errorf("marshal item %d: %v", i, err)
}
encodedVal = append(encodedVal, encodedChain...)
}
encodedVerdict, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_SET_ELEM_DATA | unix.NLA_F_NESTED, Data: encodedVal}})
if err != nil {
return nil, fmt.Errorf("marshal item %d: %v", i, err)
}
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_DATA | unix.NLA_F_NESTED, Data: encodedVerdict})
case len(v.Val) > 0:
// Since v.Val's length is not 0 then, v is a regular map element, need to add to the attributes
encodedVal, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NFTA_DATA_VALUE, Data: v.Val}})
if err != nil {
return nil, fmt.Errorf("marshal item %d: %v", i, err)
}
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_DATA | unix.NLA_F_NESTED, Data: encodedVal})
default:
// If niether of previous cases matche, it means 'e' is an element of a regular Set, no need to add to the attributes
}
encodedItem, err := netlink.MarshalAttributes(item)
if err != nil {
return nil, fmt.Errorf("marshal item %d: %v", i, err)
}
elements = append(elements, netlink.Attribute{Type: uint16(i+1) | unix.NLA_F_NESTED, Data: encodedItem})
}
encodedElem, err := netlink.MarshalAttributes(elements)
if err != nil {
return nil, fmt.Errorf("marshal elements: %v", err)
}
return []netlink.Attribute{
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
{Type: unix.NFTA_LOOKUP_SET_ID, Data: binaryutil.BigEndian.PutUint32(id)},
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
{Type: unix.NFTA_SET_ELEM_LIST_ELEMENTS | unix.NLA_F_NESTED, Data: encodedElem},
}, nil
}
// AddSet adds the specified Set.
func (cc *Conn) AddSet(s *Set, vals []SetElement) error {
cc.mu.Lock()
defer cc.mu.Unlock()
// Based on nft implementation & linux source.
// Link: https://github.com/torvalds/linux/blob/49a57857aeea06ca831043acbb0fa5e0f50602fd/net/netfilter/nf_tables_api.c#L3395
// Another reference: https://git.netfilter.org/nftables/tree/src
if s.Anonymous && !s.Constant {
return errors.New("anonymous structs must be constant")
}
if s.ID == 0 {
allocSetID++
s.ID = allocSetID
if s.Anonymous {
s.Name = "__set%d"
if s.IsMap {
s.Name = "__map%d"
}
}
}
var flags uint32
if s.Anonymous {
flags |= unix.NFT_SET_ANONYMOUS
}
if s.Constant {
flags |= unix.NFT_SET_CONSTANT
}
if s.Interval {
flags |= unix.NFT_SET_INTERVAL
}
if s.IsMap {
flags |= unix.NFT_SET_MAP
}
if s.HasTimeout {
flags |= unix.NFT_SET_TIMEOUT
}
if s.Dynamic {
flags |= unix.NFT_SET_EVAL
}
if s.Concatenation {
flags |= NFT_SET_CONCAT
}
tableInfo := []netlink.Attribute{
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
{Type: unix.NFTA_SET_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)},
{Type: unix.NFTA_SET_KEY_TYPE, Data: binaryutil.BigEndian.PutUint32(s.KeyType.nftMagic)},
{Type: unix.NFTA_SET_KEY_LEN, Data: binaryutil.BigEndian.PutUint32(s.KeyType.Bytes)},
{Type: unix.NFTA_SET_ID, Data: binaryutil.BigEndian.PutUint32(s.ID)},
}
if s.IsMap {
// Check if it is vmap case
if s.DataType.nftMagic == 1 {
// For Verdict data type, the expected magic is 0xfffff0
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_DATA_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(unix.NFT_DATA_VERDICT))},
netlink.Attribute{Type: unix.NFTA_SET_DATA_LEN, Data: binaryutil.BigEndian.PutUint32(s.DataType.Bytes)})
} else {
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_DATA_TYPE, Data: binaryutil.BigEndian.PutUint32(s.DataType.nftMagic)},
netlink.Attribute{Type: unix.NFTA_SET_DATA_LEN, Data: binaryutil.BigEndian.PutUint32(s.DataType.Bytes)})
}
}
if s.HasTimeout && s.Timeout != 0 {
// If Set's global timeout is specified, add it to set's attributes
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_TIMEOUT, Data: binaryutil.BigEndian.PutUint64(uint64(s.Timeout.Milliseconds()))})
}
if s.Constant {
// nft cli tool adds the number of elements to set/map's descriptor
// It make sense to do only if a set or map are constant, otherwise skip NFTA_SET_DESC attribute
numberOfElements, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: binaryutil.BigEndian.PutUint32(uint32(len(vals)))},
})
if err != nil {
return fmt.Errorf("fail to marshal number of elements %d: %v", len(vals), err)
}
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_SET_DESC, Data: numberOfElements})
}
if s.Concatenation {
// Length of concatenated types is a must, otherwise segfaults when executing nft list ruleset
var concatDefinition []byte
elements := ConcatSetTypeElements(s.KeyType)
for i, v := range elements {
// Marshal base type size value
valData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: binaryutil.BigEndian.PutUint32(v.Bytes)},
})
if err != nil {
return fmt.Errorf("fail to marshal element key size %d: %v", i, err)
}
// Marshal base type size description
descSize, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_SET_DESC_SIZE, Data: valData},
})
if err != nil {
return fmt.Errorf("fail to marshal base type size description: %w", err)
}
concatDefinition = append(concatDefinition, descSize...)
}
// Marshal all base type descriptions into concatenation size description
concatBytes, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NLA_F_NESTED | NFTA_SET_DESC_CONCAT, Data: concatDefinition}})
if err != nil {
return fmt.Errorf("fail to marshal concat definition %v", err)
}
// Marshal concat size description as set description
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_SET_DESC, Data: concatBytes})
}
if s.Anonymous || s.Constant || s.Interval || s.KeyByteOrder == binaryutil.BigEndian {
tableInfo = append(tableInfo,
// Semantically useless - kept for binary compatability with nft
netlink.Attribute{Type: unix.NFTA_SET_USERDATA, Data: []byte("\x00\x04\x02\x00\x00\x00")})
} else if s.KeyByteOrder == binaryutil.NativeEndian {
// Per https://git.netfilter.org/nftables/tree/src/mnl.c?id=187c6d01d35722618c2711bbc49262c286472c8f#n1165
tableInfo = append(tableInfo,
netlink.Attribute{Type: unix.NFTA_SET_USERDATA, Data: []byte("\x00\x04\x01\x00\x00\x00")})
}
if s.Counter {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_LIST_ELEM, Data: []byte("counter\x00")},
{Type: unix.NFTA_SET_ELEM_PAD | unix.NFTA_SET_ELEM_DATA, Data: []byte{}},
})
if err != nil {
return err
}
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NLA_F_NESTED | NFTA_SET_ELEM_EXPRESSIONS, Data: data})
}
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSET),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: append(extraHeader(uint8(s.Table.Family), 0), cc.marshalAttr(tableInfo)...),
})
// Set the values of the set if initial values were provided.
if len(vals) > 0 {
hdrType := unix.NFT_MSG_NEWSETELEM
elements, err := s.makeElemList(vals, s.ID)
if err != nil {
return err
}
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | hdrType),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: append(extraHeader(uint8(s.Table.Family), 0), cc.marshalAttr(elements)...),
})
}
return nil
}
// DelSet deletes a specific set, along with all elements it contains.
func (cc *Conn) DelSet(s *Set) {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSET),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(s.Table.Family), 0), data...),
})
}
// SetDeleteElements deletes data points from an nftables set.
func (cc *Conn) SetDeleteElements(s *Set, vals []SetElement) error {
cc.mu.Lock()
defer cc.mu.Unlock()
if s.Anonymous {
return errors.New("anonymous sets cannot be updated")
}
elements, err := s.makeElemList(vals, s.ID)
if err != nil {
return err
}
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSETELEM),
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
},
Data: append(extraHeader(uint8(s.Table.Family), 0), cc.marshalAttr(elements)...),
})
return nil
}
// FlushSet deletes all data points from an nftables set.
func (cc *Conn) FlushSet(s *Set) {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSETELEM),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(s.Table.Family), 0), data...),
})
}
var (
newSetHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSET)
delSetHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSET)
)
func setsFromMsg(msg netlink.Message) (*Set, error) {
if got, want1, want2 := msg.Header.Type, newSetHeaderType, delSetHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
var set Set
for ad.Next() {
switch ad.Type() {
case unix.NFTA_SET_NAME:
set.Name = ad.String()
case unix.NFTA_SET_ID:
set.ID = binary.BigEndian.Uint32(ad.Bytes())
case unix.NFTA_SET_TIMEOUT:
set.Timeout = time.Duration(time.Millisecond * time.Duration(binary.BigEndian.Uint64(ad.Bytes())))
set.HasTimeout = true
case unix.NFTA_SET_FLAGS:
flags := ad.Uint32()
set.Constant = (flags & unix.NFT_SET_CONSTANT) != 0
set.Anonymous = (flags & unix.NFT_SET_ANONYMOUS) != 0
set.Interval = (flags & unix.NFT_SET_INTERVAL) != 0
set.IsMap = (flags & unix.NFT_SET_MAP) != 0
set.HasTimeout = (flags & unix.NFT_SET_TIMEOUT) != 0
set.Concatenation = (flags & NFT_SET_CONCAT) != 0
case unix.NFTA_SET_KEY_TYPE:
nftMagic := ad.Uint32()
dt, err := parseSetDatatype(nftMagic)
if err != nil {
return nil, fmt.Errorf("could not determine data type: %w", err)
}
set.KeyType = dt
case unix.NFTA_SET_KEY_LEN:
set.KeyType.Bytes = binary.BigEndian.Uint32(ad.Bytes())
case unix.NFTA_SET_DATA_TYPE:
nftMagic := ad.Uint32()
// Special case for the data type verdict, in the message it is stored as 0xffffff00 but it is defined as 1
if nftMagic == 0xffffff00 {
set.KeyType = TypeVerdict
break
}
dt, err := parseSetDatatype(nftMagic)
if err != nil {
return nil, fmt.Errorf("could not determine data type: %w", err)
}
set.DataType = dt
case unix.NFTA_SET_DATA_LEN:
set.DataType.Bytes = binary.BigEndian.Uint32(ad.Bytes())
}
}
return &set, nil
}
func parseSetDatatype(magic uint32) (SetDatatype, error) {
types := make([]SetDatatype, 0, 32/SetConcatTypeBits)
for magic != 0 {
t := magic & SetConcatTypeMask
magic = magic >> SetConcatTypeBits
dt, ok := nftDatatypesByMagic[t]
if !ok {
return TypeInvalid, fmt.Errorf("could not determine data type %+v", dt)
}
// Because we start with the last type, we insert the later types at the front.
types = append([]SetDatatype{dt}, types...)
}
dt, err := ConcatSetType(types...)
if err != nil {
return TypeInvalid, fmt.Errorf("could not create data type: %w", err)
}
return dt, nil
}
var (
newElemHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSETELEM)
delElemHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSETELEM)
)
func elementsFromMsg(fam byte, msg netlink.Message) ([]SetElement, error) {
if got, want1, want2 := msg.Header.Type, newElemHeaderType, delElemHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
var elements []SetElement
for ad.Next() {
b := ad.Bytes()
if ad.Type() == unix.NFTA_SET_ELEM_LIST_ELEMENTS {
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
return nil, err
}
ad.ByteOrder = binary.BigEndian
for ad.Next() {
var elem SetElement
switch ad.Type() {
case unix.NFTA_LIST_ELEM:
ad.Do(elem.decode(fam))
}
elements = append(elements, elem)
}
}
}
return elements, nil
}
// GetSets returns the sets in the specified table.
func (cc *Conn) GetSets(t *Table) ([]*Set, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_SET_TABLE, Data: []byte(t.Name + "\x00")},
})
if err != nil {
return nil, err
}
message := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETSET),
Flags: netlink.Request | netlink.Acknowledge | netlink.Dump,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
}
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
return nil, fmt.Errorf("SendMessages: %v", err)
}
reply, err := receiveAckAware(conn, message.Header.Flags)
if err != nil {
return nil, fmt.Errorf("Receive: %v", err)
}
var sets []*Set
for _, msg := range reply {
s, err := setsFromMsg(msg)
if err != nil {
return nil, err
}
s.Table = &Table{Name: t.Name, Use: t.Use, Flags: t.Flags, Family: t.Family}
sets = append(sets, s)
}
return sets, nil
}
// GetSetByName returns the set in the specified table if matching name is found.
func (cc *Conn) GetSetByName(t *Table, name string) (*Set, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_SET_TABLE, Data: []byte(t.Name + "\x00")},
{Type: unix.NFTA_SET_NAME, Data: []byte(name + "\x00")},
})
if err != nil {
return nil, err
}
message := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETSET),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
}
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
return nil, fmt.Errorf("SendMessages: %w", err)
}
reply, err := receiveAckAware(conn, message.Header.Flags)
if err != nil {
return nil, fmt.Errorf("Receive: %w", err)
}
if len(reply) != 1 {
return nil, fmt.Errorf("Receive: expected to receive 1 message but got %d", len(reply))
}
rs, err := setsFromMsg(reply[0])
if err != nil {
return nil, err
}
rs.Table = &Table{Name: t.Name, Use: t.Use, Flags: t.Flags, Family: t.Family}
return rs, nil
}
// GetSetElements returns the elements in the specified set.
func (cc *Conn) GetSetElements(s *Set) ([]SetElement, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
})
if err != nil {
return nil, err
}
message := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETSETELEM),
Flags: netlink.Request | netlink.Acknowledge | netlink.Dump,
},
Data: append(extraHeader(uint8(s.Table.Family), 0), data...),
}
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
return nil, fmt.Errorf("SendMessages: %v", err)
}
reply, err := receiveAckAware(conn, message.Header.Flags)
if err != nil {
return nil, fmt.Errorf("Receive: %v", err)
}
var elems []SetElement
for _, msg := range reply {
s, err := elementsFromMsg(uint8(s.Table.Family), msg)
if err != nil {
return nil, err
}
elems = append(elems, s...)
}
return elems, nil
}

212
vendor/github.com/google/nftables/table.go generated vendored Normal file
View File

@@ -0,0 +1,212 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"fmt"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
var (
newTableHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWTABLE)
delTableHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELTABLE)
)
// TableFamily specifies the address family for this table.
type TableFamily byte
// Possible TableFamily values.
const (
TableFamilyUnspecified TableFamily = unix.NFPROTO_UNSPEC
TableFamilyINet TableFamily = unix.NFPROTO_INET
TableFamilyIPv4 TableFamily = unix.NFPROTO_IPV4
TableFamilyIPv6 TableFamily = unix.NFPROTO_IPV6
TableFamilyARP TableFamily = unix.NFPROTO_ARP
TableFamilyNetdev TableFamily = unix.NFPROTO_NETDEV
TableFamilyBridge TableFamily = unix.NFPROTO_BRIDGE
)
// A Table contains Chains. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables
type Table struct {
Name string // NFTA_TABLE_NAME
Use uint32 // NFTA_TABLE_USE (Number of chains in table)
Flags uint32 // NFTA_TABLE_FLAGS
Family TableFamily
}
// DelTable deletes a specific table, along with all chains/rules it contains.
func (cc *Conn) DelTable(t *Table) {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_TABLE_NAME, Data: []byte(t.Name + "\x00")},
{Type: unix.NFTA_TABLE_FLAGS, Data: []byte{0, 0, 0, 0}},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELTABLE),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
})
}
func (cc *Conn) addTable(t *Table, flag netlink.HeaderFlags) *Table {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_TABLE_NAME, Data: []byte(t.Name + "\x00")},
{Type: unix.NFTA_TABLE_FLAGS, Data: []byte{0, 0, 0, 0}},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWTABLE),
Flags: netlink.Request | netlink.Acknowledge | flag,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
})
return t
}
// AddTable adds the specified Table, just like `nft add table ...`.
// See also https://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables
func (cc *Conn) AddTable(t *Table) *Table {
return cc.addTable(t, netlink.Create)
}
// CreateTable create the specified Table if it do not existed.
// just like `nft create table ...`.
func (cc *Conn) CreateTable(t *Table) *Table {
return cc.addTable(t, netlink.Excl)
}
// FlushTable removes all rules in all chains within the specified Table. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables#Flushing_tables
func (cc *Conn) FlushTable(t *Table) {
cc.mu.Lock()
defer cc.mu.Unlock()
data := cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_RULE_TABLE, Data: []byte(t.Name + "\x00")},
})
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELRULE),
Flags: netlink.Request | netlink.Acknowledge,
},
Data: append(extraHeader(uint8(t.Family), 0), data...),
})
}
// ListTable returns table found for the specified name. Searches for
// the table under IPv4 family. As per nft man page: "When no address
// family is specified, ip is used by default."
func (cc *Conn) ListTable(name string) (*Table, error) {
return cc.ListTableOfFamily(name, TableFamilyIPv4)
}
// ListTableOfFamily returns table found for the specified name and table family
func (cc *Conn) ListTableOfFamily(name string, family TableFamily) (*Table, error) {
t, err := cc.listTablesOfNameAndFamily(name, family)
if err != nil {
return nil, err
}
if got, want := len(t), 1; got != want {
return nil, fmt.Errorf("expected table count %d, got %d", want, got)
}
return t[0], nil
}
// ListTables returns currently configured tables in the kernel
func (cc *Conn) ListTables() ([]*Table, error) {
return cc.ListTablesOfFamily(TableFamilyUnspecified)
}
// ListTablesOfFamily returns currently configured tables for the specified table family
// in the kernel. It lists all tables if family is TableFamilyUnspecified.
func (cc *Conn) ListTablesOfFamily(family TableFamily) ([]*Table, error) {
return cc.listTablesOfNameAndFamily("", family)
}
func (cc *Conn) listTablesOfNameAndFamily(name string, family TableFamily) ([]*Table, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
return nil, err
}
defer func() { _ = closer() }()
data := extraHeader(uint8(family), 0)
flags := netlink.Request | netlink.Dump
if name != "" {
data = append(data, cc.marshalAttr([]netlink.Attribute{
{Type: unix.NFTA_TABLE_NAME, Data: []byte(name + "\x00")},
})...)
flags = netlink.Request
}
msg := netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETTABLE),
Flags: flags,
},
Data: data,
}
response, err := conn.Execute(msg)
if err != nil {
return nil, err
}
var tables []*Table
for _, m := range response {
t, err := tableFromMsg(m)
if err != nil {
return nil, err
}
tables = append(tables, t)
}
return tables, nil
}
func tableFromMsg(msg netlink.Message) (*Table, error) {
if got, want1, want2 := msg.Header.Type, newTableHeaderType, delTableHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
var t Table
t.Family = TableFamily(msg.Data[0])
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
return nil, err
}
for ad.Next() {
switch ad.Type() {
case unix.NFTA_TABLE_NAME:
t.Name = ad.String()
case unix.NFTA_TABLE_USE:
t.Use = ad.Uint32()
case unix.NFTA_TABLE_FLAGS:
t.Flags = ad.Uint32()
}
}
return &t, nil
}

89
vendor/github.com/google/nftables/util.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import (
"encoding/binary"
"net"
"github.com/google/nftables/binaryutil"
"golang.org/x/sys/unix"
)
func extraHeader(family uint8, resID uint16) []byte {
return append([]byte{
family,
unix.NFNETLINK_V0,
}, binaryutil.BigEndian.PutUint16(resID)...)
}
// General form of address family dependent message, see
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nfnetlink.h#29
type NFGenMsg struct {
NFGenFamily uint8
Version uint8
ResourceID uint16
}
func (genmsg *NFGenMsg) Decode(b []byte) {
if len(b) < 16 {
return
}
genmsg.NFGenFamily = b[0]
genmsg.Version = b[1]
genmsg.ResourceID = binary.BigEndian.Uint16(b[2:])
}
// NetFirstAndLastIP takes the beginning address of an entire network in CIDR
// notation (e.g. 192.168.1.0/24) and returns the first and last IP addresses
// within the network (e.g. first 192.168.1.0, last 192.168.1.255).
//
// Note that these are the first and last IP addresses, not the first and last
// *usable* IP addresses (which would be 192.168.1.1 and 192.168.1.254,
// respectively, for 192.168.1.0/24).
func NetFirstAndLastIP(networkCIDR string) (first, last net.IP, err error) {
_, subnet, err := net.ParseCIDR(networkCIDR)
if err != nil {
return nil, nil, err
}
first = make(net.IP, len(subnet.IP))
last = make(net.IP, len(subnet.IP))
switch len(subnet.IP) {
case net.IPv4len:
mask := binary.BigEndian.Uint32(subnet.Mask)
ip := binary.BigEndian.Uint32(subnet.IP)
// To achieve the first IP address, we need to AND the IP with the mask.
// The AND operation will set all bits in the host part to 0.
binary.BigEndian.PutUint32(first, ip&mask)
// To achieve the last IP address, we need to OR the IP network with the inverted mask.
// The AND between the IP and the mask will set all bits in the host part to 0, keeping the network part.
// The XOR between the mask and 0xffffffff will set all bits in the host part to 1, and the network part to 0.
// The OR operation will keep the host part unchanged, and sets the host part to all 1.
binary.BigEndian.PutUint32(last, (ip&mask)|(mask^0xffffffff))
case net.IPv6len:
mask1 := binary.BigEndian.Uint64(subnet.Mask[:8])
mask2 := binary.BigEndian.Uint64(subnet.Mask[8:])
ip1 := binary.BigEndian.Uint64(subnet.IP[:8])
ip2 := binary.BigEndian.Uint64(subnet.IP[8:])
binary.BigEndian.PutUint64(first[:8], ip1&mask1)
binary.BigEndian.PutUint64(first[8:], ip2&mask2)
binary.BigEndian.PutUint64(last[:8], (ip1&mask1)|(mask1^0xffffffffffffffff))
binary.BigEndian.PutUint64(last[8:], (ip2&mask2)|(mask2^0xffffffffffffffff))
}
return first, last, nil
}

94
vendor/github.com/google/nftables/xt/info.go generated vendored Normal file
View File

@@ -0,0 +1,94 @@
package xt
import (
"golang.org/x/sys/unix"
)
// TableFamily specifies the address family of the table Match or Target Info
// data is contained in. On purpose, we don't import the expr package here in
// order to keep the option open to import this package instead into expr.
type TableFamily byte
// InfoAny is a (un)marshaling implemented by any info type.
type InfoAny interface {
marshal(fam TableFamily, rev uint32) ([]byte, error)
unmarshal(fam TableFamily, rev uint32, data []byte) error
}
// Marshal a Match or Target Info type into its binary representation.
func Marshal(fam TableFamily, rev uint32, info InfoAny) ([]byte, error) {
return info.marshal(fam, rev)
}
// Unmarshal Info binary payload into its corresponding dedicated type as
// indicated by the name argument. In several cases, unmarshalling depends on
// the specific table family the Target or Match expression with the info
// payload belongs to, as well as the specific info structure revision.
func Unmarshal(name string, fam TableFamily, rev uint32, data []byte) (InfoAny, error) {
var i InfoAny
switch name {
case "addrtype":
switch rev {
case 0:
i = &AddrType{}
case 1:
i = &AddrTypeV1{}
}
case "conntrack":
switch rev {
case 1:
i = &ConntrackMtinfo1{}
case 2:
i = &ConntrackMtinfo2{}
case 3:
i = &ConntrackMtinfo3{}
}
case "tcp":
i = &Tcp{}
case "udp":
i = &Udp{}
case "SNAT":
if fam == unix.NFPROTO_IPV4 {
i = &NatIPv4MultiRangeCompat{}
}
case "DNAT":
switch fam {
case unix.NFPROTO_IPV4:
if rev == 0 {
i = &NatIPv4MultiRangeCompat{}
break
}
fallthrough
case unix.NFPROTO_IPV6:
switch rev {
case 1:
i = &NatRange{}
case 2:
i = &NatRange2{}
}
}
case "MASQUERADE":
switch fam {
case unix.NFPROTO_IPV4:
i = &NatIPv4MultiRangeCompat{}
}
case "REDIRECT":
switch fam {
case unix.NFPROTO_IPV4:
if rev == 0 {
i = &NatIPv4MultiRangeCompat{}
break
}
fallthrough
case unix.NFPROTO_IPV6:
i = &NatRange{}
}
}
if i == nil {
i = &Unknown{}
}
if err := i.unmarshal(fam, rev, data); err != nil {
return nil, err
}
return i, nil
}

89
vendor/github.com/google/nftables/xt/match_addrtype.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
package xt
import (
"github.com/google/nftables/alignedbuff"
)
// Rev. 0, see https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/xt_addrtype.h#L38
type AddrType struct {
Source uint16
Dest uint16
InvertSource bool
InvertDest bool
}
type AddrTypeFlags uint32
const (
AddrTypeUnspec AddrTypeFlags = 1 << iota
AddrTypeUnicast
AddrTypeLocal
AddrTypeBroadcast
AddrTypeAnycast
AddrTypeMulticast
AddrTypeBlackhole
AddrTypeUnreachable
AddrTypeProhibit
AddrTypeThrow
AddrTypeNat
AddrTypeXresolve
)
// See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/xt_addrtype.h#L31
type AddrTypeV1 struct {
Source uint16
Dest uint16
Flags AddrTypeFlags
}
func (x *AddrType) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
ab.PutUint16(x.Source)
ab.PutUint16(x.Dest)
putBool32(&ab, x.InvertSource)
putBool32(&ab, x.InvertDest)
return ab.Data(), nil
}
func (x *AddrType) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if x.Source, err = ab.Uint16(); err != nil {
return nil
}
if x.Dest, err = ab.Uint16(); err != nil {
return nil
}
if x.InvertSource, err = bool32(&ab); err != nil {
return nil
}
if x.InvertDest, err = bool32(&ab); err != nil {
return nil
}
return nil
}
func (x *AddrTypeV1) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
ab.PutUint16(x.Source)
ab.PutUint16(x.Dest)
ab.PutUint32(uint32(x.Flags))
return ab.Data(), nil
}
func (x *AddrTypeV1) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if x.Source, err = ab.Uint16(); err != nil {
return nil
}
if x.Dest, err = ab.Uint16(); err != nil {
return nil
}
var flags uint32
if flags, err = ab.Uint32(); err != nil {
return nil
}
x.Flags = AddrTypeFlags(flags)
return nil
}

260
vendor/github.com/google/nftables/xt/match_conntrack.go generated vendored Normal file
View File

@@ -0,0 +1,260 @@
package xt
import (
"net"
"github.com/google/nftables/alignedbuff"
)
type ConntrackFlags uint16
const (
ConntrackState ConntrackFlags = 1 << iota
ConntrackProto
ConntrackOrigSrc
ConntrackOrigDst
ConntrackReplSrc
ConntrackReplDst
ConntrackStatus
ConntrackExpires
ConntrackOrigSrcPort
ConntrackOrigDstPort
ConntrackReplSrcPort
ConntrackReplDstPrt
ConntrackDirection
ConntrackStateAlias
)
type ConntrackMtinfoBase struct {
OrigSrcAddr net.IP
OrigSrcMask net.IPMask
OrigDstAddr net.IP
OrigDstMask net.IPMask
ReplSrcAddr net.IP
ReplSrcMask net.IPMask
ReplDstAddr net.IP
ReplDstMask net.IPMask
ExpiresMin uint32
ExpiresMax uint32
L4Proto uint16
OrigSrcPort uint16
OrigDstPort uint16
ReplSrcPort uint16
ReplDstPort uint16
MatchFlags uint16
InvertFlags uint16
}
// See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/xt_conntrack.h#L38
type ConntrackMtinfo1 struct {
ConntrackMtinfoBase
StateMask uint8
StatusMask uint8
}
// See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/xt_conntrack.h#L51
type ConntrackMtinfo2 struct {
ConntrackMtinfoBase
StateMask uint16
StatusMask uint16
}
// See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/xt_conntrack.h#L64
type ConntrackMtinfo3 struct {
ConntrackMtinfo2
OrigSrcPortHigh uint16
OrigDstPortHigh uint16
ReplSrcPortHigh uint16
ReplDstPortHigh uint16
}
func (x *ConntrackMtinfoBase) marshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
if err := putIPv46(ab, fam, x.OrigSrcAddr); err != nil {
return err
}
if err := putIPv46Mask(ab, fam, x.OrigSrcMask); err != nil {
return err
}
if err := putIPv46(ab, fam, x.OrigDstAddr); err != nil {
return err
}
if err := putIPv46Mask(ab, fam, x.OrigDstMask); err != nil {
return err
}
if err := putIPv46(ab, fam, x.ReplSrcAddr); err != nil {
return err
}
if err := putIPv46Mask(ab, fam, x.ReplSrcMask); err != nil {
return err
}
if err := putIPv46(ab, fam, x.ReplDstAddr); err != nil {
return err
}
if err := putIPv46Mask(ab, fam, x.ReplDstMask); err != nil {
return err
}
ab.PutUint32(x.ExpiresMin)
ab.PutUint32(x.ExpiresMax)
ab.PutUint16(x.L4Proto)
ab.PutUint16(x.OrigSrcPort)
ab.PutUint16(x.OrigDstPort)
ab.PutUint16(x.ReplSrcPort)
ab.PutUint16(x.ReplDstPort)
ab.PutUint16(x.MatchFlags)
ab.PutUint16(x.InvertFlags)
return nil
}
func (x *ConntrackMtinfoBase) unmarshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
var err error
if x.OrigSrcAddr, err = iPv46(ab, fam); err != nil {
return err
}
if x.OrigSrcMask, err = iPv46Mask(ab, fam); err != nil {
return err
}
if x.OrigDstAddr, err = iPv46(ab, fam); err != nil {
return err
}
if x.OrigDstMask, err = iPv46Mask(ab, fam); err != nil {
return err
}
if x.ReplSrcAddr, err = iPv46(ab, fam); err != nil {
return err
}
if x.ReplSrcMask, err = iPv46Mask(ab, fam); err != nil {
return err
}
if x.ReplDstAddr, err = iPv46(ab, fam); err != nil {
return err
}
if x.ReplDstMask, err = iPv46Mask(ab, fam); err != nil {
return err
}
if x.ExpiresMin, err = ab.Uint32(); err != nil {
return err
}
if x.ExpiresMax, err = ab.Uint32(); err != nil {
return err
}
if x.L4Proto, err = ab.Uint16(); err != nil {
return err
}
if x.OrigSrcPort, err = ab.Uint16(); err != nil {
return err
}
if x.OrigDstPort, err = ab.Uint16(); err != nil {
return err
}
if x.ReplSrcPort, err = ab.Uint16(); err != nil {
return err
}
if x.ReplDstPort, err = ab.Uint16(); err != nil {
return err
}
if x.MatchFlags, err = ab.Uint16(); err != nil {
return err
}
if x.InvertFlags, err = ab.Uint16(); err != nil {
return err
}
return nil
}
func (x *ConntrackMtinfo1) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
if err := x.ConntrackMtinfoBase.marshalAB(fam, rev, &ab); err != nil {
return nil, err
}
ab.PutUint8(x.StateMask)
ab.PutUint8(x.StatusMask)
return ab.Data(), nil
}
func (x *ConntrackMtinfo1) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if err = x.ConntrackMtinfoBase.unmarshalAB(fam, rev, &ab); err != nil {
return err
}
if x.StateMask, err = ab.Uint8(); err != nil {
return err
}
if x.StatusMask, err = ab.Uint8(); err != nil {
return err
}
return nil
}
func (x *ConntrackMtinfo2) marshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
if err := x.ConntrackMtinfoBase.marshalAB(fam, rev, ab); err != nil {
return err
}
ab.PutUint16(x.StateMask)
ab.PutUint16(x.StatusMask)
return nil
}
func (x *ConntrackMtinfo2) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
if err := x.marshalAB(fam, rev, &ab); err != nil {
return nil, err
}
return ab.Data(), nil
}
func (x *ConntrackMtinfo2) unmarshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
var err error
if err = x.ConntrackMtinfoBase.unmarshalAB(fam, rev, ab); err != nil {
return err
}
if x.StateMask, err = ab.Uint16(); err != nil {
return err
}
if x.StatusMask, err = ab.Uint16(); err != nil {
return err
}
return nil
}
func (x *ConntrackMtinfo2) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if err = x.unmarshalAB(fam, rev, &ab); err != nil {
return err
}
return nil
}
func (x *ConntrackMtinfo3) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
if err := x.ConntrackMtinfo2.marshalAB(fam, rev, &ab); err != nil {
return nil, err
}
ab.PutUint16(x.OrigSrcPortHigh)
ab.PutUint16(x.OrigDstPortHigh)
ab.PutUint16(x.ReplSrcPortHigh)
ab.PutUint16(x.ReplDstPortHigh)
return ab.Data(), nil
}
func (x *ConntrackMtinfo3) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if err = x.ConntrackMtinfo2.unmarshalAB(fam, rev, &ab); err != nil {
return err
}
if x.OrigSrcPortHigh, err = ab.Uint16(); err != nil {
return err
}
if x.OrigDstPortHigh, err = ab.Uint16(); err != nil {
return err
}
if x.ReplSrcPortHigh, err = ab.Uint16(); err != nil {
return err
}
if x.ReplDstPortHigh, err = ab.Uint16(); err != nil {
return err
}
return nil
}

74
vendor/github.com/google/nftables/xt/match_tcp.go generated vendored Normal file
View File

@@ -0,0 +1,74 @@
package xt
import (
"github.com/google/nftables/alignedbuff"
)
// Tcp is the Match.Info payload for the tcp xtables extension
// (https://wiki.nftables.org/wiki-nftables/index.php/Supported_features_compared_to_xtables#tcp).
//
// See
// https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/xt_tcpudp.h#L8
type Tcp struct {
SrcPorts [2]uint16 // min, max source port range
DstPorts [2]uint16 // min, max destination port range
Option uint8 // TCP option if non-zero
FlagsMask uint8 // TCP flags mask
FlagsCmp uint8 // TCP flags compare
InvFlags TcpInvFlagset // Inverse flags
}
type TcpInvFlagset uint8
const (
TcpInvSrcPorts TcpInvFlagset = 1 << iota
TcpInvDestPorts
TcpInvFlags
TcpInvOption
TcpInvMask TcpInvFlagset = (1 << iota) - 1
)
func (x *Tcp) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
ab.PutUint16(x.SrcPorts[0])
ab.PutUint16(x.SrcPorts[1])
ab.PutUint16(x.DstPorts[0])
ab.PutUint16(x.DstPorts[1])
ab.PutUint8(x.Option)
ab.PutUint8(x.FlagsMask)
ab.PutUint8(x.FlagsCmp)
ab.PutUint8(byte(x.InvFlags))
return ab.Data(), nil
}
func (x *Tcp) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if x.SrcPorts[0], err = ab.Uint16(); err != nil {
return err
}
if x.SrcPorts[1], err = ab.Uint16(); err != nil {
return err
}
if x.DstPorts[0], err = ab.Uint16(); err != nil {
return err
}
if x.DstPorts[1], err = ab.Uint16(); err != nil {
return err
}
if x.Option, err = ab.Uint8(); err != nil {
return err
}
if x.FlagsMask, err = ab.Uint8(); err != nil {
return err
}
if x.FlagsCmp, err = ab.Uint8(); err != nil {
return err
}
var invFlags uint8
if invFlags, err = ab.Uint8(); err != nil {
return err
}
x.InvFlags = TcpInvFlagset(invFlags)
return nil
}

57
vendor/github.com/google/nftables/xt/match_udp.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
package xt
import (
"github.com/google/nftables/alignedbuff"
)
// Tcp is the Match.Info payload for the tcp xtables extension
// (https://wiki.nftables.org/wiki-nftables/index.php/Supported_features_compared_to_xtables#tcp).
//
// See
// https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/xt_tcpudp.h#L25
type Udp struct {
SrcPorts [2]uint16 // min, max source port range
DstPorts [2]uint16 // min, max destination port range
InvFlags UdpInvFlagset // Inverse flags
}
type UdpInvFlagset uint8
const (
UdpInvSrcPorts UdpInvFlagset = 1 << iota
UdpInvDestPorts
UdpInvMask UdpInvFlagset = (1 << iota) - 1
)
func (x *Udp) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
ab.PutUint16(x.SrcPorts[0])
ab.PutUint16(x.SrcPorts[1])
ab.PutUint16(x.DstPorts[0])
ab.PutUint16(x.DstPorts[1])
ab.PutUint8(byte(x.InvFlags))
return ab.Data(), nil
}
func (x *Udp) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if x.SrcPorts[0], err = ab.Uint16(); err != nil {
return err
}
if x.SrcPorts[1], err = ab.Uint16(); err != nil {
return err
}
if x.DstPorts[0], err = ab.Uint16(); err != nil {
return err
}
if x.DstPorts[1], err = ab.Uint16(); err != nil {
return err
}
var invFlags uint8
if invFlags, err = ab.Uint8(); err != nil {
return err
}
x.InvFlags = UdpInvFlagset(invFlags)
return nil
}

106
vendor/github.com/google/nftables/xt/target_dnat.go generated vendored Normal file
View File

@@ -0,0 +1,106 @@
package xt
import (
"net"
"github.com/google/nftables/alignedbuff"
)
type NatRangeFlags uint
// See: https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/nf_nat.h#L8
const (
NatRangeMapIPs NatRangeFlags = (1 << iota)
NatRangeProtoSpecified
NatRangeProtoRandom
NatRangePersistent
NatRangeProtoRandomFully
NatRangeProtoOffset
NatRangeNetmap
NatRangeMask NatRangeFlags = (1 << iota) - 1
NatRangeProtoRandomAll = NatRangeProtoRandom | NatRangeProtoRandomFully
)
// see: https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/nf_nat.h#L38
type NatRange struct {
Flags uint // sic! platform/arch/compiler-dependent uint size
MinIP net.IP // always taking up space for an IPv6 address
MaxIP net.IP // dito
MinPort uint16
MaxPort uint16
}
// see: https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/nf_nat.h#L46
type NatRange2 struct {
NatRange
BasePort uint16
}
func (x *NatRange) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
if err := x.marshalAB(fam, rev, &ab); err != nil {
return nil, err
}
return ab.Data(), nil
}
func (x *NatRange) marshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
ab.PutUint(x.Flags)
if err := putIPv46(ab, fam, x.MinIP); err != nil {
return err
}
if err := putIPv46(ab, fam, x.MaxIP); err != nil {
return err
}
ab.PutUint16BE(x.MinPort)
ab.PutUint16BE(x.MaxPort)
return nil
}
func (x *NatRange) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
return x.unmarshalAB(fam, rev, &ab)
}
func (x *NatRange) unmarshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
var err error
if x.Flags, err = ab.Uint(); err != nil {
return err
}
if x.MinIP, err = iPv46(ab, fam); err != nil {
return err
}
if x.MaxIP, err = iPv46(ab, fam); err != nil {
return err
}
if x.MinPort, err = ab.Uint16BE(); err != nil {
return err
}
if x.MaxPort, err = ab.Uint16BE(); err != nil {
return err
}
return nil
}
func (x *NatRange2) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
if err := x.NatRange.marshalAB(fam, rev, &ab); err != nil {
return nil, err
}
ab.PutUint16BE(x.BasePort)
return ab.Data(), nil
}
func (x *NatRange2) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
var err error
if err = x.NatRange.unmarshalAB(fam, rev, &ab); err != nil {
return err
}
if x.BasePort, err = ab.Uint16BE(); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,86 @@
package xt
import (
"errors"
"net"
"github.com/google/nftables/alignedbuff"
)
// See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/nf_nat.h#L25
type NatIPv4Range struct {
Flags uint // sic!
MinIP net.IP
MaxIP net.IP
MinPort uint16
MaxPort uint16
}
// NatIPv4MultiRangeCompat despite being a slice of NAT IPv4 ranges is currently allowed to
// only hold exactly one element.
//
// See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/nf_nat.h#L33
type NatIPv4MultiRangeCompat []NatIPv4Range
func (x *NatIPv4MultiRangeCompat) marshal(fam TableFamily, rev uint32) ([]byte, error) {
ab := alignedbuff.New()
if len(*x) != 1 {
return nil, errors.New("MasqueradeIp must contain exactly one NatIPv4Range")
}
ab.PutUint(uint(len(*x)))
for _, nat := range *x {
if err := nat.marshalAB(fam, rev, &ab); err != nil {
return nil, err
}
}
return ab.Data(), nil
}
func (x *NatIPv4MultiRangeCompat) unmarshal(fam TableFamily, rev uint32, data []byte) error {
ab := alignedbuff.NewWithData(data)
l, err := ab.Uint()
if err != nil {
return err
}
nats := make(NatIPv4MultiRangeCompat, l)
for l > 0 {
l--
if err := nats[l].unmarshalAB(fam, rev, &ab); err != nil {
return err
}
}
*x = nats
return nil
}
func (x *NatIPv4Range) marshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
ab.PutUint(x.Flags)
ab.PutBytesAligned32(x.MinIP.To4(), 4)
ab.PutBytesAligned32(x.MaxIP.To4(), 4)
ab.PutUint16BE(x.MinPort)
ab.PutUint16BE(x.MaxPort)
return nil
}
func (x *NatIPv4Range) unmarshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error {
var err error
if x.Flags, err = ab.Uint(); err != nil {
return err
}
var ip []byte
if ip, err = ab.BytesAligned32(4); err != nil {
return err
}
x.MinIP = net.IP(ip)
if ip, err = ab.BytesAligned32(4); err != nil {
return err
}
x.MaxIP = net.IP(ip)
if x.MinPort, err = ab.Uint16BE(); err != nil {
return err
}
if x.MaxPort, err = ab.Uint16BE(); err != nil {
return err
}
return nil
}

17
vendor/github.com/google/nftables/xt/unknown.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
package xt
// Unknown represents the bytes Info payload for unknown Info types where no
// dedicated match/target info type has (yet) been defined.
type Unknown []byte
func (x *Unknown) marshal(fam TableFamily, rev uint32) ([]byte, error) {
// In case of unknown payload we assume its creator knows what she/he does
// and thus we don't do any alignment padding. Just take the payload "as
// is".
return *x, nil
}
func (x *Unknown) unmarshal(fam TableFamily, rev uint32, data []byte) error {
*x = data
return nil
}

64
vendor/github.com/google/nftables/xt/util.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
package xt
import (
"fmt"
"net"
"github.com/google/nftables/alignedbuff"
"golang.org/x/sys/unix"
)
func bool32(ab *alignedbuff.AlignedBuff) (bool, error) {
v, err := ab.Uint32()
if err != nil {
return false, err
}
if v != 0 {
return true, nil
}
return false, nil
}
func putBool32(ab *alignedbuff.AlignedBuff, b bool) {
if b {
ab.PutUint32(1)
return
}
ab.PutUint32(0)
}
func iPv46(ab *alignedbuff.AlignedBuff, fam TableFamily) (net.IP, error) {
ip, err := ab.BytesAligned32(16)
if err != nil {
return nil, err
}
switch fam {
case unix.NFPROTO_IPV4:
return net.IP(ip[:4]), nil
case unix.NFPROTO_IPV6:
return net.IP(ip), nil
default:
return nil, fmt.Errorf("unmarshal IP: unsupported table family %d", fam)
}
}
func iPv46Mask(ab *alignedbuff.AlignedBuff, fam TableFamily) (net.IPMask, error) {
v, err := iPv46(ab, fam)
return net.IPMask(v), err
}
func putIPv46(ab *alignedbuff.AlignedBuff, fam TableFamily, ip net.IP) error {
switch fam {
case unix.NFPROTO_IPV4:
ab.PutBytesAligned32(ip.To4(), 16)
case unix.NFPROTO_IPV6:
ab.PutBytesAligned32(ip.To16(), 16)
default:
return fmt.Errorf("marshal IP: unsupported table family %d", fam)
}
return nil
}
func putIPv46Mask(ab *alignedbuff.AlignedBuff, fam TableFamily, mask net.IPMask) error {
return putIPv46(ab, fam, net.IP(mask))
}

48
vendor/github.com/google/nftables/xt/xt.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
/*
Package xt implements dedicated types for (some) of the "Info" payload in Match
and Target expressions that bridge between the nftables and xtables worlds.
Bridging between the more unified world of nftables and the slightly
heterogenous world of xtables comes with some caveats. Unmarshalling the
extension/translation information in Match and Target expressions requires
information about the table family the information belongs to, as well as type
and type revision information. In consequence, unmarshalling the Match and
Target Info field payloads often (but not necessarily always) require the table
family and revision information, so it gets passed to the type-specific
unmarshallers.
To complicate things more, even marshalling requires knowledge about the
enclosing table family. The NatRange/NatRange2 types are an example, where it is
necessary to differentiate between IPv4 and IPv6 address marshalling. Due to
Go's net.IP habit to normally store IPv4 addresses as IPv4-compatible IPv6
addresses (see also RFC 4291, section 2.5.5.1) marshalling must be handled
differently in the context of an IPv6 table compared to an IPv4 table. In an
IPv4 table, an IPv4-compatible IPv6 address must be marshalled as a 32bit
address, whereas in an IPv6 table the IPv4 address must be marshalled as an
128bit IPv4-compatible IPv6 address. Not relying on heuristics here we avoid
behavior unexpected and most probably unknown to our API users. The net.IP habit
of storing IPv4 addresses in two different storage formats is already a source
for trouble, especially when comparing net.IPs from different Go module sources.
We won't add to this confusion. (...or maybe we can, because of it?)
An important property of all types of Info extension/translation payloads is
that their marshalling and unmarshalling doesn't follow netlink's TLV
(tag-length-value) architecture. Instead, Info payloads a basically plain binary
blobs of their respective type-specific data structures, so host
platform/architecture alignment and data type sizes apply. The alignedbuff
package implements the different required data types alignments.
Please note that Info payloads are always padded at their end to the next uint64
alignment. Kernel code is checking for the padded payload size and will reject
payloads not correctly padded at their ends.
Most of the time, we find explifcitly sized (unsigned integer) data types.
However, there are notable exceptions where "unsigned int" is used: on 64bit
platforms this mostly translates into 32bit(!). This differs from Go mapping
uint to uint64 instead. This package currently clamps its mapping of C's
"unsigned int" to Go's uint32 for marshalling and unmarshalling. If in the
future 128bit platforms with a differently sized C unsigned int should come into
production, then the alignedbuff package will need to be adapted accordingly, as
it abstracts away this data type handling.
*/
package xt

41
vendor/github.com/google/uuid/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,41 @@
# Changelog
## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16)
### Features
* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3))
### Bug Fixes
* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06))
* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6))
## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12)
### Features
* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29))
## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26)
### Features
* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4))
### Fixes
* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior)
## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18)
### Bug Fixes
* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0))
## Changelog

26
vendor/github.com/google/uuid/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,26 @@
# How to contribute
We definitely welcome patches and contribution to this project!
### Tips
Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org).
Always try to include a test case! If it is not possible or not necessary,
please explain why in the pull request description.
### Releasing
Commits that would precipitate a SemVer change, as described in the Conventional
Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action)
to create a release candidate pull request. Once submitted, `release-please`
will create a release.
For tips on how to work with `release-please`, see its documentation.
### Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).
You may have already signed it for other Google projects.

9
vendor/github.com/google/uuid/CONTRIBUTORS generated vendored Normal file
View File

@@ -0,0 +1,9 @@
Paul Borman <borman@google.com>
bmatsuo
shawnps
theory
jboverfelt
dsymonds
cd1
wallclockbuilder
dansouza

27
vendor/github.com/google/uuid/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2009,2014 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

21
vendor/github.com/google/uuid/README.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
# uuid
The uuid package generates and inspects UUIDs based on
[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)
and DCE 1.1: Authentication and Security Services.
This package is based on the github.com/pborman/uuid package (previously named
code.google.com/p/go-uuid). It differs from these earlier packages in that
a UUID is a 16 byte array rather than a byte slice. One loss due to this
change is the ability to represent an invalid UUID (vs a NIL UUID).
###### Install
```sh
go get github.com/google/uuid
```
###### Documentation
[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid)
Full `go doc` style documentation for the package can be viewed online without
installing this package by using the GoDoc site here:
http://pkg.go.dev/github.com/google/uuid

80
vendor/github.com/google/uuid/dce.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"fmt"
"os"
)
// A Domain represents a Version 2 domain
type Domain byte
// Domain constants for DCE Security (Version 2) UUIDs.
const (
Person = Domain(0)
Group = Domain(1)
Org = Domain(2)
)
// NewDCESecurity returns a DCE Security (Version 2) UUID.
//
// The domain should be one of Person, Group or Org.
// On a POSIX system the id should be the users UID for the Person
// domain and the users GID for the Group. The meaning of id for
// the domain Org or on non-POSIX systems is site defined.
//
// For a given domain/id pair the same token may be returned for up to
// 7 minutes and 10 seconds.
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
uuid, err := NewUUID()
if err == nil {
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
uuid[9] = byte(domain)
binary.BigEndian.PutUint32(uuid[0:], id)
}
return uuid, err
}
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
// domain with the id returned by os.Getuid.
//
// NewDCESecurity(Person, uint32(os.Getuid()))
func NewDCEPerson() (UUID, error) {
return NewDCESecurity(Person, uint32(os.Getuid()))
}
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
// domain with the id returned by os.Getgid.
//
// NewDCESecurity(Group, uint32(os.Getgid()))
func NewDCEGroup() (UUID, error) {
return NewDCESecurity(Group, uint32(os.Getgid()))
}
// Domain returns the domain for a Version 2 UUID. Domains are only defined
// for Version 2 UUIDs.
func (uuid UUID) Domain() Domain {
return Domain(uuid[9])
}
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
// UUIDs.
func (uuid UUID) ID() uint32 {
return binary.BigEndian.Uint32(uuid[0:4])
}
func (d Domain) String() string {
switch d {
case Person:
return "Person"
case Group:
return "Group"
case Org:
return "Org"
}
return fmt.Sprintf("Domain%d", int(d))
}

12
vendor/github.com/google/uuid/doc.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package uuid generates and inspects UUIDs.
//
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
// Services.
//
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
// maps or compared directly.
package uuid

59
vendor/github.com/google/uuid/hash.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"crypto/md5"
"crypto/sha1"
"hash"
)
// Well known namespace IDs and UUIDs
var (
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
Nil UUID // empty UUID, all zeros
// The Max UUID is special form of UUID that is specified to have all 128 bits set to 1.
Max = UUID{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
}
)
// NewHash returns a new UUID derived from the hash of space concatenated with
// data generated by h. The hash should be at least 16 byte in length. The
// first 16 bytes of the hash are used to form the UUID. The version of the
// UUID will be the lower 4 bits of version. NewHash is used to implement
// NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset()
h.Write(space[:]) //nolint:errcheck
h.Write(data) //nolint:errcheck
s := h.Sum(nil)
var uuid UUID
copy(uuid[:], s)
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
return uuid
}
// NewMD5 returns a new MD5 (Version 3) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(md5.New(), space, data, 3)
func NewMD5(space UUID, data []byte) UUID {
return NewHash(md5.New(), space, data, 3)
}
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(sha1.New(), space, data, 5)
func NewSHA1(space UUID, data []byte) UUID {
return NewHash(sha1.New(), space, data, 5)
}

38
vendor/github.com/google/uuid/marshal.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "fmt"
// MarshalText implements encoding.TextMarshaler.
func (uuid UUID) MarshalText() ([]byte, error) {
var js [36]byte
encodeHex(js[:], uuid)
return js[:], nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err != nil {
return err
}
*uuid = id
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (uuid UUID) MarshalBinary() ([]byte, error) {
return uuid[:], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (uuid *UUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(uuid[:], data)
return nil
}

90
vendor/github.com/google/uuid/node.go generated vendored Normal file
View File

@@ -0,0 +1,90 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"sync"
)
var (
nodeMu sync.Mutex
ifname string // name of interface being used
nodeID [6]byte // hardware for version 1 UUIDs
zeroID [6]byte // nodeID with only 0's
)
// NodeInterface returns the name of the interface from which the NodeID was
// derived. The interface "user" is returned if the NodeID was set by
// SetNodeID.
func NodeInterface() string {
defer nodeMu.Unlock()
nodeMu.Lock()
return ifname
}
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
// If name is "" then the first usable interface found will be used or a random
// Node ID will be generated. If a named interface cannot be found then false
// is returned.
//
// SetNodeInterface never fails when name is "".
func SetNodeInterface(name string) bool {
defer nodeMu.Unlock()
nodeMu.Lock()
return setNodeInterface(name)
}
func setNodeInterface(name string) bool {
iname, addr := getHardwareInterface(name) // null implementation for js
if iname != "" && addr != nil {
ifname = iname
copy(nodeID[:], addr)
return true
}
// We found no interfaces with a valid hardware address. If name
// does not specify a specific interface generate a random Node ID
// (section 4.1.6)
if name == "" {
ifname = "random"
randomBits(nodeID[:])
return true
}
return false
}
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
// if not already set.
func NodeID() []byte {
defer nodeMu.Unlock()
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nid := nodeID
return nid[:]
}
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
// of id are used. If id is less than 6 bytes then false is returned and the
// Node ID is not set.
func SetNodeID(id []byte) bool {
if len(id) < 6 {
return false
}
defer nodeMu.Unlock()
nodeMu.Lock()
copy(nodeID[:], id)
ifname = "user"
return true
}
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) NodeID() []byte {
var node [6]byte
copy(node[:], uuid[10:])
return node[:]
}

12
vendor/github.com/google/uuid/node_js.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js
package uuid
// getHardwareInterface returns nil values for the JS version of the code.
// This removes the "net" dependency, because it is not used in the browser.
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
func getHardwareInterface(name string) (string, []byte) { return "", nil }

33
vendor/github.com/google/uuid/node_net.go generated vendored Normal file
View File

@@ -0,0 +1,33 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !js
package uuid
import "net"
var interfaces []net.Interface // cached list of interfaces
// getHardwareInterface returns the name and hardware address of interface name.
// If name is "" then the name and hardware address of one of the system's
// interfaces is returned. If no interfaces are found (name does not exist or
// there are no interfaces) then "", nil is returned.
//
// Only addresses of at least 6 bytes are returned.
func getHardwareInterface(name string) (string, []byte) {
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
if err != nil {
return "", nil
}
}
for _, ifs := range interfaces {
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
return ifs.Name, ifs.HardwareAddr
}
}
return "", nil
}

118
vendor/github.com/google/uuid/null.go generated vendored Normal file
View File

@@ -0,0 +1,118 @@
// Copyright 2021 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"bytes"
"database/sql/driver"
"encoding/json"
"fmt"
)
var jsonNull = []byte("null")
// NullUUID represents a UUID that may be null.
// NullUUID implements the SQL driver.Scanner interface so
// it can be used as a scan destination:
//
// var u uuid.NullUUID
// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
// ...
// if u.Valid {
// // use u.UUID
// } else {
// // NULL value
// }
//
type NullUUID struct {
UUID UUID
Valid bool // Valid is true if UUID is not NULL
}
// Scan implements the SQL driver.Scanner interface.
func (nu *NullUUID) Scan(value interface{}) error {
if value == nil {
nu.UUID, nu.Valid = Nil, false
return nil
}
err := nu.UUID.Scan(value)
if err != nil {
nu.Valid = false
return err
}
nu.Valid = true
return nil
}
// Value implements the driver Valuer interface.
func (nu NullUUID) Value() (driver.Value, error) {
if !nu.Valid {
return nil, nil
}
// Delegate to UUID Value function
return nu.UUID.Value()
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (nu NullUUID) MarshalBinary() ([]byte, error) {
if nu.Valid {
return nu.UUID[:], nil
}
return []byte(nil), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (nu *NullUUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(nu.UUID[:], data)
nu.Valid = true
return nil
}
// MarshalText implements encoding.TextMarshaler.
func (nu NullUUID) MarshalText() ([]byte, error) {
if nu.Valid {
return nu.UUID.MarshalText()
}
return jsonNull, nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (nu *NullUUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err != nil {
nu.Valid = false
return err
}
nu.UUID = id
nu.Valid = true
return nil
}
// MarshalJSON implements json.Marshaler.
func (nu NullUUID) MarshalJSON() ([]byte, error) {
if nu.Valid {
return json.Marshal(nu.UUID)
}
return jsonNull, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (nu *NullUUID) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, jsonNull) {
*nu = NullUUID{}
return nil // valid null UUID
}
err := json.Unmarshal(data, &nu.UUID)
nu.Valid = err == nil
return err
}

59
vendor/github.com/google/uuid/sql.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"database/sql/driver"
"fmt"
)
// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
switch src := src.(type) {
case nil:
return nil
case string:
// if an empty UUID comes from a table, we return a null UUID
if src == "" {
return nil
}
// see Parse for required string format
u, err := Parse(src)
if err != nil {
return fmt.Errorf("Scan: %v", err)
}
*uuid = u
case []byte:
// if an empty UUID comes from a table, we return a null UUID
if len(src) == 0 {
return nil
}
// assumes a simple slice of bytes if 16 bytes
// otherwise attempts to parse
if len(src) != 16 {
return uuid.Scan(string(src))
}
copy((*uuid)[:], src)
default:
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
}
return nil
}
// Value implements sql.Valuer so that UUIDs can be written to databases
// transparently. Currently, UUIDs map to strings. Please consult
// database-specific driver documentation for matching types.
func (uuid UUID) Value() (driver.Value, error) {
return uuid.String(), nil
}

134
vendor/github.com/google/uuid/time.go generated vendored Normal file
View File

@@ -0,0 +1,134 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"sync"
"time"
)
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
// 1582.
type Time int64
const (
lillian = 2299160 // Julian day of 15 Oct 1582
unix = 2440587 // Julian day of 1 Jan 1970
epoch = unix - lillian // Days between epochs
g1582 = epoch * 86400 // seconds between epochs
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
)
var (
timeMu sync.Mutex
lasttime uint64 // last time we returned
clockSeq uint16 // clock sequence for this run
timeNow = time.Now // for testing
)
// UnixTime converts t the number of seconds and nanoseconds using the Unix
// epoch of 1 Jan 1970.
func (t Time) UnixTime() (sec, nsec int64) {
sec = int64(t - g1582ns100)
nsec = (sec % 10000000) * 100
sec /= 10000000
return sec, nsec
}
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
// clock sequence as well as adjusting the clock sequence as needed. An error
// is returned if the current time cannot be determined.
func GetTime() (Time, uint16, error) {
defer timeMu.Unlock()
timeMu.Lock()
return getTime()
}
func getTime() (Time, uint16, error) {
t := timeNow()
// If we don't have a clock sequence already, set one.
if clockSeq == 0 {
setClockSequence(-1)
}
now := uint64(t.UnixNano()/100) + g1582ns100
// If time has gone backwards with this clock sequence then we
// increment the clock sequence
if now <= lasttime {
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
}
lasttime = now
return Time(now), clockSeq, nil
}
// ClockSequence returns the current clock sequence, generating one if not
// already set. The clock sequence is only used for Version 1 UUIDs.
//
// The uuid package does not use global static storage for the clock sequence or
// the last time a UUID was generated. Unless SetClockSequence is used, a new
// random clock sequence is generated the first time a clock sequence is
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
func ClockSequence() int {
defer timeMu.Unlock()
timeMu.Lock()
return clockSequence()
}
func clockSequence() int {
if clockSeq == 0 {
setClockSequence(-1)
}
return int(clockSeq & 0x3fff)
}
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
// -1 causes a new sequence to be generated.
func SetClockSequence(seq int) {
defer timeMu.Unlock()
timeMu.Lock()
setClockSequence(seq)
}
func setClockSequence(seq int) {
if seq == -1 {
var b [2]byte
randomBits(b[:]) // clock sequence
seq = int(b[0])<<8 | int(b[1])
}
oldSeq := clockSeq
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
if oldSeq != clockSeq {
lasttime = 0
}
}
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs.
func (uuid UUID) Time() Time {
var t Time
switch uuid.Version() {
case 6:
time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110
t = Time(time)
case 7:
time := binary.BigEndian.Uint64(uuid[:8])
t = Time((time>>16)*10000 + g1582ns100)
default: // forward compatible
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
t = Time(time)
}
return t
}
// ClockSequence returns the clock sequence encoded in uuid.
// The clock sequence is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) ClockSequence() int {
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
}

Some files were not shown because too many files have changed in this diff Show More