Update knftables, with new sigs.k8s.io module name
This commit is contained in:
201
vendor/github.com/danwinship/knftables/LICENSE
generated
vendored
201
vendor/github.com/danwinship/knftables/LICENSE
generated
vendored
@@ -1,201 +0,0 @@
|
||||
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.
|
177
vendor/github.com/danwinship/knftables/README.md
generated
vendored
177
vendor/github.com/danwinship/knftables/README.md
generated
vendored
@@ -1,177 +0,0 @@
|
||||
# knftables: a golang nftables library
|
||||
|
||||
This is a library for using nftables from Go.
|
||||
|
||||
It is not intended to support arbitrary use cases, but instead
|
||||
specifically focuses on supporing Kubernetes components which are
|
||||
using nftables in the way that nftables is supposed to be used (as
|
||||
opposed to using nftables in a naively-translated-from-iptables way,
|
||||
or using nftables to do totally valid things that aren't the sorts of
|
||||
things Kubernetes components are likely to need to do).
|
||||
|
||||
It is still under development and is not API stable.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an `Interface` object to manage operations on a single nftables
|
||||
table:
|
||||
|
||||
```golang
|
||||
nft, err := knftables.New(knftables.IPv4Family, "my-table")
|
||||
if err != nil {
|
||||
return fmt.Errorf("no nftables support: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
You can use the `List`, `ListRules`, and `ListElements` methods on the
|
||||
`Interface` to check if objects exist. `List` returns the names of
|
||||
`"chains"`, `"sets"`, or `"maps"` in the table, while `ListElements`
|
||||
returns `Element` objects and `ListRules` returns *partial* `Rule`
|
||||
objects.
|
||||
|
||||
```golang
|
||||
chains, err := nft.List(ctx, "chains")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not list chains: %v", err)
|
||||
}
|
||||
|
||||
FIXME
|
||||
|
||||
elements, err := nft.ListElements(ctx, "map", "mymap")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not list map elements: %v", err)
|
||||
}
|
||||
|
||||
FIXME
|
||||
```
|
||||
|
||||
To make changes, create a `Transaction`, add the appropriate
|
||||
operations to the transaction, and then call `nft.Run` on it:
|
||||
|
||||
```golang
|
||||
tx := nft.NewTransaction()
|
||||
|
||||
tx.Add(&knftables.Chain{
|
||||
Name: "mychain",
|
||||
Comment: knftables.PtrTo("this is my chain"),
|
||||
})
|
||||
tx.Flush(&knftables.Chain{
|
||||
Name: "mychain",
|
||||
})
|
||||
|
||||
var destIP net.IP
|
||||
var destPort uint16
|
||||
...
|
||||
tx.Add(&knftables.Rule{
|
||||
Chain: "mychain",
|
||||
Rule: knftables.Concat(
|
||||
"ip daddr", destIP,
|
||||
"ip protocol", "tcp",
|
||||
"th port", destPort,
|
||||
"jump", destChain,
|
||||
)
|
||||
})
|
||||
|
||||
err := nft.Run(context, tx)
|
||||
```
|
||||
|
||||
If any operation in the transaction would fail, then `Run()` will
|
||||
return an error and the entire transaction will be ignored. You can
|
||||
use the `knftables.IsNotFound()` and `knftables.IsAlreadyExists()`
|
||||
methods to check for those well-known error types. In a large
|
||||
transaction, there is no supported way to determine exactly which
|
||||
operation failed.
|
||||
|
||||
## `knftables.Transaction` operations
|
||||
|
||||
`knftables.Transaction` operations correspond to the top-level commands
|
||||
in the `nft` binary. Currently-supported operations are:
|
||||
|
||||
- `tx.Add()`: adds an object, which may already exist, as with `nft add`
|
||||
- `tx.Create()`: creates an object, which must not already exist, as with `nft create`
|
||||
- `tx.Flush()`: flushes the contents of a table/chain/set/map, as with `nft flush`
|
||||
- `tx.Delete()`: deletes an object, as with `nft delete`
|
||||
- `tx.Insert()`: inserts a rule before another rule, as with `nft insert rule`
|
||||
- `tx.Replace()`: replaces a rule, as with `nft replace rule`
|
||||
|
||||
## Objects
|
||||
|
||||
The `Transaction` methods take arguments of type `knftables.Object`.
|
||||
The currently-supported objects are:
|
||||
|
||||
- `Table`
|
||||
- `Chain`
|
||||
- `Rule`
|
||||
- `Set`
|
||||
- `Map`
|
||||
- `Element`
|
||||
|
||||
Optional fields in objects can be filled in with the help of the
|
||||
`PtrTo()` function, which just returns a pointer to its argument.
|
||||
|
||||
`Concat()` can be used to concatenate a series of strings, `[]string`
|
||||
arrays, and other arguments (including numbers, `net.IP`s /
|
||||
`net.IPNet`s, and anything else that can be formatted usefully via
|
||||
`fmt.Sprintf("%s")`) together into a single string. This is often
|
||||
useful when constructing `Rule`s.
|
||||
|
||||
## `knftables.Fake`
|
||||
|
||||
There is a fake (in-memory) implementation of `knftables.Interface`
|
||||
for use in unit tests. Use `knftables.NewFake()` instead of
|
||||
`knftables.New()` to create it, and then it should work mostly the
|
||||
same. See `fake.go` for more details of the public APIs for examining
|
||||
the current state of the fake nftables database.
|
||||
|
||||
Note that at the present time, `fake.Run()` is not actually
|
||||
transactional, so unit tests that rely on things not being changed if
|
||||
a transaction fails partway through will not work as expected.
|
||||
|
||||
## Missing APIs
|
||||
|
||||
Various top-level object types are not yet supported (notably the
|
||||
"stateful objects" like `counter`).
|
||||
|
||||
Most IPTables libraries have an API for "add this rule only if it
|
||||
doesn't already exist", but that does not seem as useful in nftables
|
||||
(or at least "in nftables as used by Kubernetes-ish components that
|
||||
aren't just blindly copying over old iptables APIs"), because chains
|
||||
tend to have static rules and dynamic sets/maps, rather than having
|
||||
dynamic rules. If you aren't sure if a chain has the correct rules,
|
||||
you can just `Flush` it and recreate all of the rules.
|
||||
|
||||
I've considered changing the semantics of `tx.Add(obj)` so that
|
||||
`obj.Handle` is filled in with the new object's handle on return from
|
||||
`Run()`, for ease of deleting later. (This would be implemented by
|
||||
using the `--handle` (`-a`) and `--echo` (`-e`) flags to `nft add`.)
|
||||
However, this would require potentially difficult parsing of the `nft`
|
||||
output. `ListRules` fills in the handles of the rules it returns, so
|
||||
it's possible to find out a rule's handle after the fact that way. For
|
||||
other supported object types, either handles don't exist (`Element`)
|
||||
or you don't really need to know their handles because it's possible
|
||||
to delete by name instead (`Table`, `Chain`, `Set`, `Map`).
|
||||
|
||||
The "destroy" (delete-without-ENOENT) command that exists in newer
|
||||
versions of `nft` is not currently supported because it would be
|
||||
unexpectedly heavyweight to emulate on systems that don't have it, so
|
||||
it is better (for now) to force callers to implement it by hand.
|
||||
|
||||
`ListRules` returns `Rule` objects without the `Rule` field filled in,
|
||||
because it uses the JSON API to list the rules, but there is no easy
|
||||
way to convert the JSON rule representation back into plaintext form.
|
||||
This means that it is only useful when either (a) you know the order
|
||||
of the rules in the chain, but want to know their handles, or (b) you
|
||||
can recognize the rules you are looking for by their comments, rather
|
||||
than the rule bodies.
|
||||
|
||||
# Design Notes
|
||||
|
||||
The library works by invoking the `nft` binary. "Write" operations are
|
||||
implemented with the ordinary plain-text API, while "read" operations
|
||||
are implemented with the JSON API, for parseability.
|
||||
|
||||
The fact that the API uses functions and objects (e.g.
|
||||
`tx.Add(&knftables.Chain{...})`) rather than just specifying everything
|
||||
as textual input to `nft` (e.g. `tx.Exec("add chain ...")`) is mostly
|
||||
just because it's _much_ easier to have a fake implementation for unit
|
||||
tests this way.
|
93
vendor/github.com/danwinship/knftables/error.go
generated
vendored
93
vendor/github.com/danwinship/knftables/error.go
generated
vendored
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type nftablesError struct {
|
||||
wrapped error
|
||||
msg string
|
||||
errno syscall.Errno
|
||||
}
|
||||
|
||||
// wrapError wraps an error resulting from running nft
|
||||
func wrapError(err error) error {
|
||||
nerr := &nftablesError{wrapped: err, msg: err.Error()}
|
||||
if ee, ok := err.(*exec.ExitError); ok {
|
||||
if len(ee.Stderr) > 0 {
|
||||
nerr.msg = string(ee.Stderr)
|
||||
eol := strings.Index(nerr.msg, "\n")
|
||||
// The nft binary does not call setlocale() and so will return
|
||||
// English error strings regardless of the locale.
|
||||
enoent := strings.Index(nerr.msg, "No such file or directory")
|
||||
eexist := strings.Index(nerr.msg, "File exists")
|
||||
if enoent != -1 && (enoent < eol || eol == -1) {
|
||||
nerr.errno = syscall.ENOENT
|
||||
} else if eexist != -1 && (eexist < eol || eol == -1) {
|
||||
nerr.errno = syscall.EEXIST
|
||||
}
|
||||
}
|
||||
}
|
||||
return nerr
|
||||
}
|
||||
|
||||
// notFoundError returns an nftablesError with the given message for which IsNotFound will
|
||||
// return true.
|
||||
func notFoundError(format string, args ...interface{}) error {
|
||||
return &nftablesError{msg: fmt.Sprintf(format, args...), errno: syscall.ENOENT}
|
||||
}
|
||||
|
||||
// existsError returns an nftablesError with the given message for which IsAlreadyExists
|
||||
// will return true.
|
||||
func existsError(format string, args ...interface{}) error {
|
||||
return &nftablesError{msg: fmt.Sprintf(format, args...), errno: syscall.EEXIST}
|
||||
}
|
||||
|
||||
func (nerr *nftablesError) Error() string {
|
||||
return nerr.msg
|
||||
}
|
||||
|
||||
func (nerr *nftablesError) Unwrap() error {
|
||||
return nerr.wrapped
|
||||
}
|
||||
|
||||
// IsNotFound tests if err corresponds to an nftables "not found" error of any sort.
|
||||
// (e.g., in response to a "delete rule" command, this might indicate that the rule
|
||||
// doesn't exist, or the chain doesn't exist, or the table doesn't exist.)
|
||||
func IsNotFound(err error) bool {
|
||||
var nerr *nftablesError
|
||||
if errors.As(err, &nerr) {
|
||||
return nerr.errno == syscall.ENOENT
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAlreadyExists tests if err corresponds to an nftables "already exists" error (e.g.
|
||||
// when doing a "create" rather than an "add").
|
||||
func IsAlreadyExists(err error) bool {
|
||||
var nerr *nftablesError
|
||||
if errors.As(err, &nerr) {
|
||||
return nerr.errno == syscall.EEXIST
|
||||
}
|
||||
return false
|
||||
}
|
48
vendor/github.com/danwinship/knftables/exec.go
generated
vendored
48
vendor/github.com/danwinship/knftables/exec.go
generated
vendored
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// execer is a mockable wrapper around os/exec.
|
||||
type execer interface {
|
||||
// LookPath wraps exec.LookPath
|
||||
LookPath(file string) (string, error)
|
||||
|
||||
// Run runs cmd as with cmd.Output(). If an error occurs, and the process outputs
|
||||
// stderr, then that output will be returned in the error.
|
||||
Run(cmd *exec.Cmd) (string, error)
|
||||
}
|
||||
|
||||
// realExec implements execer by actually using os/exec
|
||||
type realExec struct{}
|
||||
|
||||
// LookPath is part of execer
|
||||
func (_ realExec) LookPath(file string) (string, error) {
|
||||
return exec.LookPath(file)
|
||||
}
|
||||
|
||||
// Run is part of execer
|
||||
func (_ realExec) Run(cmd *exec.Cmd) (string, error) {
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
err = wrapError(err)
|
||||
}
|
||||
return string(out), err
|
||||
}
|
495
vendor/github.com/danwinship/knftables/fake.go
generated
vendored
495
vendor/github.com/danwinship/knftables/fake.go
generated
vendored
@@ -1,495 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Fake is a fake implementation of Interface
|
||||
type Fake struct {
|
||||
nftContext
|
||||
|
||||
nextHandle int
|
||||
|
||||
// Table contains the Interface's table. This will be `nil` until you `tx.Add()`
|
||||
// the table.
|
||||
Table *FakeTable
|
||||
}
|
||||
|
||||
// FakeTable wraps Table for the Fake implementation
|
||||
type FakeTable struct {
|
||||
Table
|
||||
|
||||
// Chains contains the table's chains, keyed by name
|
||||
Chains map[string]*FakeChain
|
||||
|
||||
// Sets contains the table's sets, keyed by name
|
||||
Sets map[string]*FakeSet
|
||||
|
||||
// Maps contains the table's maps, keyed by name
|
||||
Maps map[string]*FakeMap
|
||||
}
|
||||
|
||||
// FakeChain wraps Chain for the Fake implementation
|
||||
type FakeChain struct {
|
||||
Chain
|
||||
|
||||
// Rules contains the chain's rules, in order
|
||||
Rules []*Rule
|
||||
}
|
||||
|
||||
// FakeSet wraps Set for the Fake implementation
|
||||
type FakeSet struct {
|
||||
Set
|
||||
|
||||
// Elements contains the set's elements. You can also use the FakeSet's
|
||||
// FindElement() method to see if a particular element is present.
|
||||
Elements []*Element
|
||||
}
|
||||
|
||||
// FakeMap wraps Set for the Fake implementation
|
||||
type FakeMap struct {
|
||||
Map
|
||||
|
||||
// Elements contains the map's elements. You can also use the FakeMap's
|
||||
// FindElement() method to see if a particular element is present.
|
||||
Elements []*Element
|
||||
}
|
||||
|
||||
// NewFake creates a new fake Interface, for unit tests
|
||||
func NewFake(family Family, table string) *Fake {
|
||||
return &Fake{
|
||||
nftContext: nftContext{
|
||||
family: family,
|
||||
table: table,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var _ Interface = &Fake{}
|
||||
|
||||
// List is part of Interface.
|
||||
func (fake *Fake) List(ctx context.Context, objectType string) ([]string, error) {
|
||||
if fake.Table == nil {
|
||||
return nil, notFoundError("no such table %q", fake.table)
|
||||
}
|
||||
|
||||
var result []string
|
||||
|
||||
switch objectType {
|
||||
case "chain", "chains":
|
||||
for name := range fake.Table.Chains {
|
||||
result = append(result, name)
|
||||
}
|
||||
case "set", "sets":
|
||||
for name := range fake.Table.Sets {
|
||||
result = append(result, name)
|
||||
}
|
||||
case "map", "maps":
|
||||
for name := range fake.Table.Maps {
|
||||
result = append(result, name)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported object type %q", objectType)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ListRules is part of Interface
|
||||
func (fake *Fake) ListRules(ctx context.Context, chain string) ([]*Rule, error) {
|
||||
if fake.Table == nil {
|
||||
return nil, notFoundError("no such chain %q", chain)
|
||||
}
|
||||
ch := fake.Table.Chains[chain]
|
||||
if ch == nil {
|
||||
return nil, notFoundError("no such chain %q", chain)
|
||||
}
|
||||
return ch.Rules, nil
|
||||
}
|
||||
|
||||
// ListElements is part of Interface
|
||||
func (fake *Fake) ListElements(ctx context.Context, objectType, name string) ([]*Element, error) {
|
||||
if fake.Table == nil {
|
||||
return nil, notFoundError("no such %s %q", objectType, name)
|
||||
}
|
||||
if objectType == "set" {
|
||||
s := fake.Table.Sets[name]
|
||||
if s != nil {
|
||||
return s.Elements, nil
|
||||
}
|
||||
} else if objectType == "map" {
|
||||
m := fake.Table.Maps[name]
|
||||
if m != nil {
|
||||
return m.Elements, nil
|
||||
}
|
||||
}
|
||||
return nil, notFoundError("no such %s %q", objectType, name)
|
||||
}
|
||||
|
||||
// NewTransaction is part of Interface
|
||||
func (fake *Fake) NewTransaction() *Transaction {
|
||||
return &Transaction{nftContext: &fake.nftContext}
|
||||
}
|
||||
|
||||
// Run is part of Interface
|
||||
func (fake *Fake) Run(ctx context.Context, tx *Transaction) error {
|
||||
if tx.err != nil {
|
||||
return tx.err
|
||||
}
|
||||
|
||||
// FIXME: not actually transactional!
|
||||
|
||||
for _, op := range tx.operations {
|
||||
// If the table hasn't been created, and this isn't a Table operation, then fail
|
||||
if fake.Table == nil {
|
||||
if _, ok := op.obj.(*Table); !ok {
|
||||
return notFoundError("no such table \"%s %s\"", fake.family, fake.table)
|
||||
}
|
||||
}
|
||||
|
||||
if op.verb == addVerb || op.verb == createVerb || op.verb == insertVerb {
|
||||
fake.nextHandle++
|
||||
}
|
||||
|
||||
switch obj := op.obj.(type) {
|
||||
case *Table:
|
||||
err := checkExists(op.verb, "table", fake.table, fake.Table != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch op.verb {
|
||||
case flushVerb:
|
||||
fake.Table = nil
|
||||
fallthrough
|
||||
case addVerb, createVerb:
|
||||
if fake.Table != nil {
|
||||
continue
|
||||
}
|
||||
table := *obj
|
||||
table.Handle = PtrTo(fake.nextHandle)
|
||||
fake.Table = &FakeTable{
|
||||
Table: table,
|
||||
Chains: make(map[string]*FakeChain),
|
||||
Sets: make(map[string]*FakeSet),
|
||||
Maps: make(map[string]*FakeMap),
|
||||
}
|
||||
case deleteVerb:
|
||||
fake.Table = nil
|
||||
default:
|
||||
return fmt.Errorf("unhandled operation %q", op.verb)
|
||||
}
|
||||
|
||||
case *Chain:
|
||||
existingChain := fake.Table.Chains[obj.Name]
|
||||
err := checkExists(op.verb, "chain", obj.Name, existingChain != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch op.verb {
|
||||
case addVerb, createVerb:
|
||||
if existingChain != nil {
|
||||
continue
|
||||
}
|
||||
chain := *obj
|
||||
chain.Handle = PtrTo(fake.nextHandle)
|
||||
fake.Table.Chains[obj.Name] = &FakeChain{
|
||||
Chain: chain,
|
||||
}
|
||||
case flushVerb:
|
||||
existingChain.Rules = nil
|
||||
case deleteVerb:
|
||||
// FIXME delete-by-handle
|
||||
delete(fake.Table.Chains, obj.Name)
|
||||
default:
|
||||
return fmt.Errorf("unhandled operation %q", op.verb)
|
||||
}
|
||||
|
||||
case *Rule:
|
||||
existingChain := fake.Table.Chains[obj.Chain]
|
||||
if existingChain == nil {
|
||||
return notFoundError("no such chain %q", obj.Chain)
|
||||
}
|
||||
if op.verb == deleteVerb {
|
||||
i := findRule(existingChain.Rules, *obj.Handle)
|
||||
if i == -1 {
|
||||
return notFoundError("no rule with handle %d", *obj.Handle)
|
||||
}
|
||||
existingChain.Rules = append(existingChain.Rules[:i], existingChain.Rules[i+1:]...)
|
||||
continue
|
||||
}
|
||||
|
||||
rule := *obj
|
||||
refRule := -1
|
||||
if rule.Handle != nil {
|
||||
refRule = findRule(existingChain.Rules, *obj.Handle)
|
||||
if refRule == -1 {
|
||||
return notFoundError("no rule with handle %d", *obj.Handle)
|
||||
}
|
||||
} else if obj.Index != nil {
|
||||
if *obj.Index >= len(existingChain.Rules) {
|
||||
return notFoundError("no rule with index %d", *obj.Index)
|
||||
}
|
||||
refRule = *obj.Index
|
||||
}
|
||||
|
||||
switch op.verb {
|
||||
case addVerb:
|
||||
if refRule == -1 {
|
||||
existingChain.Rules = append(existingChain.Rules, &rule)
|
||||
} else {
|
||||
existingChain.Rules = append(existingChain.Rules[:refRule+1], append([]*Rule{&rule}, existingChain.Rules[refRule+1:]...)...)
|
||||
}
|
||||
rule.Handle = PtrTo(fake.nextHandle)
|
||||
case insertVerb:
|
||||
if refRule == -1 {
|
||||
existingChain.Rules = append([]*Rule{&rule}, existingChain.Rules...)
|
||||
} else {
|
||||
existingChain.Rules = append(existingChain.Rules[:refRule], append([]*Rule{&rule}, existingChain.Rules[refRule:]...)...)
|
||||
}
|
||||
rule.Handle = PtrTo(fake.nextHandle)
|
||||
case replaceVerb:
|
||||
existingChain.Rules[refRule] = &rule
|
||||
default:
|
||||
return fmt.Errorf("unhandled operation %q", op.verb)
|
||||
}
|
||||
|
||||
case *Set:
|
||||
existingSet := fake.Table.Sets[obj.Name]
|
||||
err := checkExists(op.verb, "set", obj.Name, existingSet != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch op.verb {
|
||||
case addVerb, createVerb:
|
||||
if existingSet != nil {
|
||||
continue
|
||||
}
|
||||
set := *obj
|
||||
set.Handle = PtrTo(fake.nextHandle)
|
||||
fake.Table.Sets[obj.Name] = &FakeSet{
|
||||
Set: set,
|
||||
}
|
||||
case flushVerb:
|
||||
existingSet.Elements = nil
|
||||
case deleteVerb:
|
||||
// FIXME delete-by-handle
|
||||
delete(fake.Table.Sets, obj.Name)
|
||||
default:
|
||||
return fmt.Errorf("unhandled operation %q", op.verb)
|
||||
}
|
||||
case *Map:
|
||||
existingMap := fake.Table.Maps[obj.Name]
|
||||
err := checkExists(op.verb, "map", obj.Name, existingMap != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch op.verb {
|
||||
case addVerb:
|
||||
if existingMap != nil {
|
||||
continue
|
||||
}
|
||||
mapObj := *obj
|
||||
mapObj.Handle = PtrTo(fake.nextHandle)
|
||||
fake.Table.Maps[obj.Name] = &FakeMap{
|
||||
Map: mapObj,
|
||||
}
|
||||
case flushVerb:
|
||||
existingMap.Elements = nil
|
||||
case deleteVerb:
|
||||
// FIXME delete-by-handle
|
||||
delete(fake.Table.Maps, obj.Name)
|
||||
default:
|
||||
return fmt.Errorf("unhandled operation %q", op.verb)
|
||||
}
|
||||
case *Element:
|
||||
if len(obj.Value) == 0 {
|
||||
existingSet := fake.Table.Sets[obj.Set]
|
||||
if existingSet == nil {
|
||||
return notFoundError("no such set %q", obj.Set)
|
||||
}
|
||||
switch op.verb {
|
||||
case addVerb, createVerb:
|
||||
element := *obj
|
||||
if i := findElement(existingSet.Elements, element.Key); i != -1 {
|
||||
if op.verb == createVerb {
|
||||
return existsError("element %q already exists", strings.Join(element.Key, " . "))
|
||||
}
|
||||
existingSet.Elements[i] = &element
|
||||
} else {
|
||||
existingSet.Elements = append(existingSet.Elements, &element)
|
||||
}
|
||||
case deleteVerb:
|
||||
element := *obj
|
||||
if i := findElement(existingSet.Elements, element.Key); i != -1 {
|
||||
existingSet.Elements = append(existingSet.Elements[:i], existingSet.Elements[i+1:]...)
|
||||
} else {
|
||||
return notFoundError("no such element %q", strings.Join(element.Key, " . "))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unhandled operation %q", op.verb)
|
||||
}
|
||||
} else {
|
||||
existingMap := fake.Table.Maps[obj.Map]
|
||||
if existingMap == nil {
|
||||
return notFoundError("no such map %q", obj.Map)
|
||||
}
|
||||
switch op.verb {
|
||||
case addVerb, createVerb:
|
||||
element := *obj
|
||||
if i := findElement(existingMap.Elements, element.Key); i != -1 {
|
||||
if op.verb == createVerb {
|
||||
return existsError("element %q already exists", strings.Join(element.Key, ". "))
|
||||
}
|
||||
existingMap.Elements[i] = &element
|
||||
} else {
|
||||
existingMap.Elements = append(existingMap.Elements, &element)
|
||||
}
|
||||
case deleteVerb:
|
||||
element := *obj
|
||||
if i := findElement(existingMap.Elements, element.Key); i != -1 {
|
||||
existingMap.Elements = append(existingMap.Elements[:i], existingMap.Elements[i+1:]...)
|
||||
} else {
|
||||
return notFoundError("no such element %q", strings.Join(element.Key, " . "))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unhandled operation %q", op.verb)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unhandled object type %T", op.obj)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkExists(verb verb, objectType, name string, exists bool) error {
|
||||
switch verb {
|
||||
case addVerb:
|
||||
// It's fine if the object either exists or doesn't
|
||||
return nil
|
||||
case createVerb:
|
||||
if exists {
|
||||
return existsError("%s %q already exists", objectType, name)
|
||||
}
|
||||
default:
|
||||
if !exists {
|
||||
return notFoundError("no such %s %q", objectType, name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dump dumps the current contents of fake, in a way that looks like an nft transaction,
|
||||
// but not actually guaranteed to be usable as such. (e.g., chains may be referenced
|
||||
// before they are created, etc)
|
||||
func (fake *Fake) Dump() string {
|
||||
if fake.Table == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := &strings.Builder{}
|
||||
|
||||
table := fake.Table
|
||||
table.writeOperation(addVerb, &fake.nftContext, buf)
|
||||
|
||||
for _, cname := range sortKeys(table.Chains) {
|
||||
ch := table.Chains[cname]
|
||||
ch.writeOperation(addVerb, &fake.nftContext, buf)
|
||||
|
||||
for _, rule := range ch.Rules {
|
||||
// Avoid outputing handles
|
||||
dumpRule := *rule
|
||||
dumpRule.Handle = nil
|
||||
dumpRule.Index = nil
|
||||
dumpRule.writeOperation(addVerb, &fake.nftContext, buf)
|
||||
}
|
||||
}
|
||||
|
||||
for _, sname := range sortKeys(table.Sets) {
|
||||
s := table.Sets[sname]
|
||||
s.writeOperation(addVerb, &fake.nftContext, buf)
|
||||
|
||||
for _, element := range s.Elements {
|
||||
element.writeOperation(addVerb, &fake.nftContext, buf)
|
||||
}
|
||||
}
|
||||
for _, mname := range sortKeys(table.Maps) {
|
||||
m := table.Maps[mname]
|
||||
m.writeOperation(addVerb, &fake.nftContext, buf)
|
||||
|
||||
for _, element := range m.Elements {
|
||||
element.writeOperation(addVerb, &fake.nftContext, buf)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func sortKeys[K ~string, V any](m map[K]V) []K {
|
||||
keys := make([]K, 0, len(m))
|
||||
for key := range m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
|
||||
return keys
|
||||
}
|
||||
|
||||
func findRule(rules []*Rule, handle int) int {
|
||||
for i := range rules {
|
||||
if rules[i].Handle != nil && *rules[i].Handle == handle {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func findElement(elements []*Element, key []string) int {
|
||||
for i := range elements {
|
||||
if reflect.DeepEqual(elements[i].Key, key) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// FindElement finds an element of the set with the given key. If there is no matching
|
||||
// element, it returns nil.
|
||||
func (s *FakeSet) FindElement(key ...string) *Element {
|
||||
index := findElement(s.Elements, key)
|
||||
if index == -1 {
|
||||
return nil
|
||||
}
|
||||
return s.Elements[index]
|
||||
}
|
||||
|
||||
// FindElement finds an element of the map with the given key. If there is no matching
|
||||
// element, it returns nil.
|
||||
func (m *FakeMap) FindElement(key ...string) *Element {
|
||||
index := findElement(m.Elements, key)
|
||||
if index == -1 {
|
||||
return nil
|
||||
}
|
||||
return m.Elements[index]
|
||||
}
|
436
vendor/github.com/danwinship/knftables/nftables.go
generated
vendored
436
vendor/github.com/danwinship/knftables/nftables.go
generated
vendored
@@ -1,436 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// Interface is an interface for running nftables commands against a given family and table.
|
||||
type Interface interface {
|
||||
// NewTransaction returns a new (empty) Transaction
|
||||
NewTransaction() *Transaction
|
||||
|
||||
// Run runs a Transaction and returns the result. The IsNotFound and
|
||||
// IsAlreadyExists methods can be used to test the result.
|
||||
Run(ctx context.Context, tx *Transaction) error
|
||||
|
||||
// List returns a list of the names of the objects of objectType ("chain", "set",
|
||||
// or "map") in the table. If there are no such objects, this will return an empty
|
||||
// list and no error.
|
||||
List(ctx context.Context, objectType string) ([]string, error)
|
||||
|
||||
// ListRules returns a list of the rules in a chain, in order. Note that at the
|
||||
// present time, the Rule objects will have their `Comment` and `Handle` fields
|
||||
// filled in, but *not* the actual `Rule` field. So this can only be used to find
|
||||
// the handles of rules if they have unique comments to recognize them by, or if
|
||||
// you know the order of the rules within the chain. If the chain exists but
|
||||
// contains no rules, this will return an empty list and no error.
|
||||
ListRules(ctx context.Context, chain string) ([]*Rule, error)
|
||||
|
||||
// ListElements returns a list of the elements in a set or map. (objectType should
|
||||
// be "set" or "map".) If the set/map exists but contains no elements, this will
|
||||
// return an empty list and no error.
|
||||
ListElements(ctx context.Context, objectType, name string) ([]*Element, error)
|
||||
}
|
||||
|
||||
type nftContext struct {
|
||||
family Family
|
||||
table string
|
||||
|
||||
// noObjectComments is true if comments on Table/Chain/Set/Map are not supported.
|
||||
// (Comments on Rule and Element are always supported.)
|
||||
noObjectComments bool
|
||||
}
|
||||
|
||||
// realNFTables is an implementation of Interface
|
||||
type realNFTables struct {
|
||||
nftContext
|
||||
|
||||
exec execer
|
||||
path string
|
||||
}
|
||||
|
||||
// for unit tests
|
||||
func newInternal(family Family, table string, execer execer) (Interface, error) {
|
||||
var err error
|
||||
|
||||
nft := &realNFTables{
|
||||
nftContext: nftContext{
|
||||
family: family,
|
||||
table: table,
|
||||
},
|
||||
|
||||
exec: execer,
|
||||
}
|
||||
|
||||
nft.path, err = nft.exec.LookPath("nft")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find nftables binary: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(nft.path, "--check", "add", "table", string(nft.family), nft.table)
|
||||
_, err = nft.exec.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not run nftables command: %w", err)
|
||||
}
|
||||
|
||||
cmd = exec.Command(nft.path, "--check", "add", "table", string(nft.family), nft.table,
|
||||
"{", "comment", `"test"`, "}",
|
||||
)
|
||||
_, err = nft.exec.Run(cmd)
|
||||
if err != nil {
|
||||
nft.noObjectComments = true
|
||||
}
|
||||
|
||||
return nft, nil
|
||||
}
|
||||
|
||||
// New creates a new nftables.Interface for interacting with the given table. If nftables
|
||||
// is not available/usable on the current host, it will return an error.
|
||||
func New(family Family, table string) (Interface, error) {
|
||||
return newInternal(family, table, realExec{})
|
||||
}
|
||||
|
||||
// NewTransaction is part of Interface
|
||||
func (nft *realNFTables) NewTransaction() *Transaction {
|
||||
return &Transaction{nftContext: &nft.nftContext}
|
||||
}
|
||||
|
||||
// Run is part of Interface
|
||||
func (nft *realNFTables) Run(ctx context.Context, tx *Transaction) error {
|
||||
if tx.err != nil {
|
||||
return tx.err
|
||||
}
|
||||
|
||||
buf, err := tx.asCommandBuf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, nft.path, "-f", "-")
|
||||
cmd.Stdin = buf
|
||||
_, err = nft.exec.Run(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
// jsonVal looks up key in json; if it exists and is of type T, it returns (json[key], true).
|
||||
// Otherwise it returns (_, false).
|
||||
func jsonVal[T any](json map[string]interface{}, key string) (T, bool) {
|
||||
if ifVal, exists := json[key]; exists {
|
||||
tVal, ok := ifVal.(T)
|
||||
return tVal, ok
|
||||
} else {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
}
|
||||
|
||||
// getJSONObjects takes the output of "nft -j list", validates it, and returns an array
|
||||
// of just the objects of objectType.
|
||||
func getJSONObjects(listOutput, objectType string) ([]map[string]interface{}, error) {
|
||||
// listOutput should contain JSON looking like:
|
||||
//
|
||||
// {
|
||||
// "nftables": [
|
||||
// {
|
||||
// "metainfo": {
|
||||
// "json_schema_version": 1,
|
||||
// ...
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "chain": {
|
||||
// "family": "ip",
|
||||
// "table": "kube-proxy",
|
||||
// "name": "KUBE-SERVICES",
|
||||
// "handle": 3
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "chain": {
|
||||
// "family": "ip",
|
||||
// "table": "kube-proxy",
|
||||
// "name": "KUBE-NODEPORTS",
|
||||
// "handle": 4
|
||||
// }
|
||||
// },
|
||||
// ...
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// In this case, given objectType "chain", we would return
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "family": "ip",
|
||||
// "table": "kube-proxy",
|
||||
// "name": "KUBE-SERVICES",
|
||||
// "handle": 3
|
||||
// },
|
||||
// {
|
||||
// "family": "ip",
|
||||
// "table": "kube-proxy",
|
||||
// "name": "KUBE-NODEPORTS",
|
||||
// "handle": 4
|
||||
// },
|
||||
// ...
|
||||
// ]
|
||||
|
||||
jsonResult := map[string][]map[string]map[string]interface{}{}
|
||||
if err := json.Unmarshal([]byte(listOutput), &jsonResult); err != nil {
|
||||
return nil, fmt.Errorf("could not parse nft output: %w", err)
|
||||
}
|
||||
|
||||
nftablesResult := jsonResult["nftables"]
|
||||
if nftablesResult == nil || len(nftablesResult) == 0 {
|
||||
return nil, fmt.Errorf("could not find result in nft output %q", listOutput)
|
||||
}
|
||||
metainfo := nftablesResult[0]["metainfo"]
|
||||
if metainfo == nil {
|
||||
return nil, fmt.Errorf("could not find metadata in nft output %q", listOutput)
|
||||
}
|
||||
// json_schema_version is an integer but `json.Unmarshal()` will have parsed it as
|
||||
// a float64 since we didn't tell it otherwise.
|
||||
if version, ok := jsonVal[float64](metainfo, "json_schema_version"); !ok || version != 1.0 {
|
||||
return nil, fmt.Errorf("could not find supported json_schema_version in nft output %q", listOutput)
|
||||
}
|
||||
|
||||
var objects []map[string]interface{}
|
||||
for _, objContainer := range nftablesResult {
|
||||
obj := objContainer[objectType]
|
||||
if obj != nil {
|
||||
objects = append(objects, obj)
|
||||
}
|
||||
}
|
||||
return objects, nil
|
||||
}
|
||||
|
||||
// List is part of Interface.
|
||||
func (nft *realNFTables) List(ctx context.Context, objectType string) ([]string, error) {
|
||||
// All currently-existing nftables object types have plural forms that are just
|
||||
// the singular form plus 's'.
|
||||
var typeSingular, typePlural string
|
||||
if objectType[len(objectType)-1] == 's' {
|
||||
typeSingular = objectType[:len(objectType)-1]
|
||||
typePlural = objectType
|
||||
} else {
|
||||
typeSingular = objectType
|
||||
typePlural = objectType + "s"
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, nft.path, "--json", "list", typePlural, string(nft.family))
|
||||
out, err := nft.exec.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run nft: %w", err)
|
||||
}
|
||||
|
||||
objects, err := getJSONObjects(out, typeSingular)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []string
|
||||
for _, obj := range objects {
|
||||
objTable, _ := jsonVal[string](obj, "table")
|
||||
if objTable != nft.table {
|
||||
continue
|
||||
}
|
||||
|
||||
if name, ok := jsonVal[string](obj, "name"); ok {
|
||||
result = append(result, name)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ListRules is part of Interface
|
||||
func (nft *realNFTables) ListRules(ctx context.Context, chain string) ([]*Rule, error) {
|
||||
cmd := exec.CommandContext(ctx, nft.path, "--json", "list", "chain", string(nft.family), nft.table, chain)
|
||||
out, err := nft.exec.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run nft: %w", err)
|
||||
}
|
||||
|
||||
jsonRules, err := getJSONObjects(out, "rule")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse JSON output: %w", err)
|
||||
}
|
||||
|
||||
rules := make([]*Rule, 0, len(jsonRules))
|
||||
for _, jsonRule := range jsonRules {
|
||||
rule := &Rule{
|
||||
Chain: chain,
|
||||
}
|
||||
|
||||
// handle is written as an integer in nft's output, but json.Unmarshal
|
||||
// will have parsed it as a float64. (Handles are uint64s, but they are
|
||||
// assigned consecutively starting from 1, so as long as fewer than 2**53
|
||||
// nftables objects have been created since boot time, we won't run into
|
||||
// float64-vs-uint64 precision issues.)
|
||||
if handle, ok := jsonVal[float64](jsonRule, "handle"); ok {
|
||||
rule.Handle = PtrTo(int(handle))
|
||||
}
|
||||
if comment, ok := jsonVal[string](jsonRule, "comment"); ok {
|
||||
rule.Comment = &comment
|
||||
}
|
||||
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
// ListElements is part of Interface
|
||||
func (nft *realNFTables) ListElements(ctx context.Context, objectType, name string) ([]*Element, error) {
|
||||
cmd := exec.CommandContext(ctx, nft.path, "--json", "list", objectType, string(nft.family), nft.table, name)
|
||||
out, err := nft.exec.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run nft: %w", err)
|
||||
}
|
||||
|
||||
jsonSetsOrMaps, err := getJSONObjects(out, objectType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse JSON output: %w", err)
|
||||
}
|
||||
if len(jsonSetsOrMaps) != 1 {
|
||||
return nil, fmt.Errorf("unexpected JSON output from nft (multiple results)")
|
||||
}
|
||||
|
||||
jsonElements, _ := jsonVal[[]interface{}](jsonSetsOrMaps[0], "elem")
|
||||
elements := make([]*Element, 0, len(jsonElements))
|
||||
for _, jsonElement := range jsonElements {
|
||||
var key, value interface{}
|
||||
|
||||
elem := &Element{}
|
||||
if objectType == "set" {
|
||||
elem.Set = name
|
||||
key = jsonElement
|
||||
} else {
|
||||
elem.Map = name
|
||||
tuple, ok := jsonElement.([]interface{})
|
||||
if !ok || len(tuple) != 2 {
|
||||
return nil, fmt.Errorf("unexpected JSON output from nft (elem is not [key,val]: %q)", jsonElement)
|
||||
}
|
||||
key, value = tuple[0], tuple[1]
|
||||
}
|
||||
|
||||
// If the element has a comment, then key will be a compound object like:
|
||||
//
|
||||
// {
|
||||
// "elem": {
|
||||
// "val": "192.168.0.1",
|
||||
// "comment": "this is a comment"
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// (Where "val" contains the value that key would have held if there was no
|
||||
// comment.)
|
||||
if obj, ok := key.(map[string]interface{}); ok {
|
||||
if compoundElem, ok := jsonVal[map[string]interface{}](obj, "elem"); ok {
|
||||
if key, ok = jsonVal[interface{}](compoundElem, "val"); !ok {
|
||||
return nil, fmt.Errorf("unexpected JSON output from nft (elem with no val: %q)", jsonElement)
|
||||
}
|
||||
if comment, ok := jsonVal[string](compoundElem, "comment"); ok {
|
||||
elem.Comment = &comment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elem.Key, err = parseElementValue(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value != nil {
|
||||
elem.Value, err = parseElementValue(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
elements = append(elements, elem)
|
||||
}
|
||||
return elements, nil
|
||||
}
|
||||
|
||||
// parseElementValue parses a JSON element key/value, handling concatenations, and
|
||||
// converting numeric or "verdict" values to strings.
|
||||
func parseElementValue(json interface{}) ([]string, error) {
|
||||
// json can be:
|
||||
//
|
||||
// - a single string, e.g. "192.168.1.3"
|
||||
//
|
||||
// - a single number, e.g. 80
|
||||
//
|
||||
// - a concatenation, expressed as an object containing an array of simple
|
||||
// values:
|
||||
// {
|
||||
// "concat": [
|
||||
// "192.168.1.3",
|
||||
// "tcp",
|
||||
// 80
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// - a verdict (for a vmap value), expressed as an object:
|
||||
// {
|
||||
// "drop": null
|
||||
// }
|
||||
//
|
||||
// {
|
||||
// "goto": {
|
||||
// "target": "destchain"
|
||||
// }
|
||||
// }
|
||||
|
||||
switch val := json.(type) {
|
||||
case string:
|
||||
return []string{val}, nil
|
||||
case float64:
|
||||
return []string{fmt.Sprintf("%d", int(val))}, nil
|
||||
case map[string]interface{}:
|
||||
if concat, _ := jsonVal[[]interface{}](val, "concat"); concat != nil {
|
||||
vals := make([]string, len(concat))
|
||||
for i := range concat {
|
||||
if str, ok := concat[i].(string); ok {
|
||||
vals[i] = str
|
||||
} else if num, ok := concat[i].(float64); ok {
|
||||
vals[i] = fmt.Sprintf("%d", int(num))
|
||||
} else {
|
||||
return nil, fmt.Errorf("could not parse element value %q", concat[i])
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
} else if len(val) == 1 {
|
||||
var verdict string
|
||||
// We just checked that len(val) == 1, so this loop body will only
|
||||
// run once
|
||||
for k, v := range val {
|
||||
if v == nil {
|
||||
verdict = k
|
||||
} else if target, ok := v.(map[string]interface{}); ok {
|
||||
verdict = fmt.Sprintf("%s %s", k, target["target"])
|
||||
}
|
||||
}
|
||||
return []string{verdict}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not parse element value %q", json)
|
||||
}
|
377
vendor/github.com/danwinship/knftables/objects.go
generated
vendored
377
vendor/github.com/danwinship/knftables/objects.go
generated
vendored
@@ -1,377 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Object implementation for Table
|
||||
func (table *Table) validate(verb verb) error {
|
||||
switch verb {
|
||||
case addVerb, createVerb, flushVerb:
|
||||
if table.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
case deleteVerb:
|
||||
// Handle can be nil or non-nil
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for tables", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *Table) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && table.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete table %s handle %d", ctx.family, *table.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
// All other cases refer to the table by name
|
||||
fmt.Fprintf(writer, "%s table %s %s", verb, ctx.family, ctx.table)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
if table.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " { comment %q ; }", *table.Comment)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
// Object implementation for Chain
|
||||
func (chain *Chain) validate(verb verb) error {
|
||||
if chain.Hook == nil && (chain.Type != nil || chain.Priority != nil) {
|
||||
return fmt.Errorf("regular chain %q must not specify Type or Priority", chain.Name)
|
||||
} else if chain.Hook != nil && (chain.Type == nil || chain.Priority == nil) {
|
||||
return fmt.Errorf("base chain %q must specify Type and Priority", chain.Name)
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, createVerb, flushVerb:
|
||||
if chain.Name == "" {
|
||||
return fmt.Errorf("no name specified for chain")
|
||||
}
|
||||
if chain.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
case deleteVerb:
|
||||
if chain.Name == "" && chain.Handle == nil {
|
||||
return fmt.Errorf("must specify either name or handle")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for chains", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (chain *Chain) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && chain.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete chain %s %s handle %d", ctx.family, ctx.table, *chain.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s chain %s %s %s", verb, ctx.family, ctx.table, chain.Name)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
if chain.Type != nil || (chain.Comment != nil && !ctx.noObjectComments) {
|
||||
fmt.Fprintf(writer, " {")
|
||||
|
||||
if chain.Type != nil {
|
||||
// Parse the priority to a number if we can, because older
|
||||
// versions of nft don't accept certain named priorities
|
||||
// in all contexts (eg, "dstnat" priority in the "output"
|
||||
// hook).
|
||||
if priority, err := ParsePriority(ctx.family, string(*chain.Priority)); err == nil {
|
||||
fmt.Fprintf(writer, " type %s hook %s priority %d ;", *chain.Type, *chain.Hook, priority)
|
||||
} else {
|
||||
fmt.Fprintf(writer, " type %s hook %s priority %s ;", *chain.Type, *chain.Hook, *chain.Priority)
|
||||
}
|
||||
}
|
||||
if chain.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " comment %q ;", *chain.Comment)
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
// Object implementation for Rule
|
||||
func (rule *Rule) validate(verb verb) error {
|
||||
if rule.Chain == "" {
|
||||
return fmt.Errorf("no chain name specified for rule")
|
||||
}
|
||||
|
||||
if rule.Index != nil && rule.Handle != nil {
|
||||
return fmt.Errorf("cannot specify both Index and Handle")
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, insertVerb:
|
||||
if rule.Rule == "" {
|
||||
return fmt.Errorf("no rule specified")
|
||||
}
|
||||
case replaceVerb:
|
||||
if rule.Rule == "" {
|
||||
return fmt.Errorf("no rule specified")
|
||||
}
|
||||
if rule.Handle == nil {
|
||||
return fmt.Errorf("must specify Handle with %s", verb)
|
||||
}
|
||||
case deleteVerb:
|
||||
if rule.Handle == nil {
|
||||
return fmt.Errorf("must specify Handle with %s", verb)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for rules", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rule *Rule) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
fmt.Fprintf(writer, "%s rule %s %s %s", verb, ctx.family, ctx.table, rule.Chain)
|
||||
if rule.Index != nil {
|
||||
fmt.Fprintf(writer, " index %d", *rule.Index)
|
||||
} else if rule.Handle != nil {
|
||||
fmt.Fprintf(writer, " handle %d", *rule.Handle)
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, insertVerb, replaceVerb:
|
||||
fmt.Fprintf(writer, " %s", rule.Rule)
|
||||
|
||||
if rule.Comment != nil {
|
||||
fmt.Fprintf(writer, " comment %q", *rule.Comment)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
// Object implementation for Set
|
||||
func (set *Set) validate(verb verb) error {
|
||||
switch verb {
|
||||
case addVerb, createVerb:
|
||||
if (set.Type == "" && set.TypeOf == "") || (set.Type != "" && set.TypeOf != "") {
|
||||
return fmt.Errorf("set must specify either Type or TypeOf")
|
||||
}
|
||||
if set.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
fallthrough
|
||||
case flushVerb:
|
||||
if set.Name == "" {
|
||||
return fmt.Errorf("no name specified for set")
|
||||
}
|
||||
case deleteVerb:
|
||||
if set.Name == "" && set.Handle == nil {
|
||||
return fmt.Errorf("must specify either name or handle")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for sets", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (set *Set) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && set.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete set %s %s handle %d", ctx.family, ctx.table, *set.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s set %s %s %s", verb, ctx.family, ctx.table, set.Name)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
fmt.Fprintf(writer, " {")
|
||||
|
||||
if set.Type != "" {
|
||||
fmt.Fprintf(writer, " type %s ;", set.Type)
|
||||
} else {
|
||||
fmt.Fprintf(writer, " typeof %s ;", set.TypeOf)
|
||||
}
|
||||
|
||||
if len(set.Flags) != 0 {
|
||||
fmt.Fprintf(writer, " flags ")
|
||||
for i := range set.Flags {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(writer, ",")
|
||||
}
|
||||
fmt.Fprintf(writer, "%s", set.Flags[i])
|
||||
}
|
||||
fmt.Fprintf(writer, " ;")
|
||||
}
|
||||
|
||||
if set.Timeout != nil {
|
||||
fmt.Fprintf(writer, " timeout %ds ;", int64(set.Timeout.Seconds()))
|
||||
}
|
||||
if set.GCInterval != nil {
|
||||
fmt.Fprintf(writer, " gc-interval %ds ;", int64(set.GCInterval.Seconds()))
|
||||
}
|
||||
if set.Size != nil {
|
||||
fmt.Fprintf(writer, " size %d ;", *set.Size)
|
||||
}
|
||||
if set.Policy != nil {
|
||||
fmt.Fprintf(writer, " policy %s ;", *set.Policy)
|
||||
}
|
||||
if set.AutoMerge != nil && *set.AutoMerge {
|
||||
fmt.Fprintf(writer, " auto-merge ;")
|
||||
}
|
||||
|
||||
if set.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " comment %q ;", *set.Comment)
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }")
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
// Object implementation for Map
|
||||
func (mapObj *Map) validate(verb verb) error {
|
||||
switch verb {
|
||||
case addVerb, createVerb:
|
||||
if (mapObj.Type == "" && mapObj.TypeOf == "") || (mapObj.Type != "" && mapObj.TypeOf != "") {
|
||||
return fmt.Errorf("map must specify either Type or TypeOf")
|
||||
}
|
||||
if mapObj.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
fallthrough
|
||||
case flushVerb:
|
||||
if mapObj.Name == "" {
|
||||
return fmt.Errorf("no name specified for map")
|
||||
}
|
||||
case deleteVerb:
|
||||
if mapObj.Name == "" && mapObj.Handle == nil {
|
||||
return fmt.Errorf("must specify either name or handle")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for maps", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mapObj *Map) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && mapObj.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete map %s %s handle %d", ctx.family, ctx.table, *mapObj.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s map %s %s %s", verb, ctx.family, ctx.table, mapObj.Name)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
fmt.Fprintf(writer, " {")
|
||||
|
||||
if mapObj.Type != "" {
|
||||
fmt.Fprintf(writer, " type %s ;", mapObj.Type)
|
||||
} else {
|
||||
fmt.Fprintf(writer, " typeof %s ;", mapObj.TypeOf)
|
||||
}
|
||||
|
||||
if len(mapObj.Flags) != 0 {
|
||||
fmt.Fprintf(writer, " flags ")
|
||||
for i := range mapObj.Flags {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(writer, ",")
|
||||
}
|
||||
fmt.Fprintf(writer, "%s", mapObj.Flags[i])
|
||||
}
|
||||
fmt.Fprintf(writer, " ;")
|
||||
}
|
||||
|
||||
if mapObj.Timeout != nil {
|
||||
fmt.Fprintf(writer, " timeout %ds ;", int64(mapObj.Timeout.Seconds()))
|
||||
}
|
||||
if mapObj.GCInterval != nil {
|
||||
fmt.Fprintf(writer, " gc-interval %ds ;", int64(mapObj.GCInterval.Seconds()))
|
||||
}
|
||||
if mapObj.Size != nil {
|
||||
fmt.Fprintf(writer, " size %d ;", *mapObj.Size)
|
||||
}
|
||||
if mapObj.Policy != nil {
|
||||
fmt.Fprintf(writer, " policy %s ;", *mapObj.Policy)
|
||||
}
|
||||
|
||||
if mapObj.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " comment %q ;", *mapObj.Comment)
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }")
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
// Object implementation for Element
|
||||
func (element *Element) validate(verb verb) error {
|
||||
if element.Map == "" && element.Set == "" {
|
||||
return fmt.Errorf("no set/map name specified for element")
|
||||
} else if element.Set != "" && element.Map != "" {
|
||||
return fmt.Errorf("element specifies both a set name and a map name")
|
||||
}
|
||||
|
||||
if len(element.Key) == 0 {
|
||||
return fmt.Errorf("no key specified for element")
|
||||
}
|
||||
if element.Set != "" && len(element.Value) != 0 {
|
||||
return fmt.Errorf("map value specified for set element")
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, createVerb:
|
||||
if element.Map != "" && len(element.Value) == 0 {
|
||||
return fmt.Errorf("no map value specified for map element")
|
||||
}
|
||||
case deleteVerb:
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for elements", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (element *Element) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
name := element.Set
|
||||
if name == "" {
|
||||
name = element.Map
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s element %s %s %s { %s", verb, ctx.family, ctx.table, name,
|
||||
strings.Join(element.Key, " . "))
|
||||
|
||||
if verb == addVerb || verb == createVerb {
|
||||
if element.Comment != nil {
|
||||
fmt.Fprintf(writer, " comment %q", *element.Comment)
|
||||
}
|
||||
|
||||
if len(element.Value) != 0 {
|
||||
fmt.Fprintf(writer, " : %s", strings.Join(element.Value, " . "))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }\n")
|
||||
}
|
138
vendor/github.com/danwinship/knftables/transaction.go
generated
vendored
138
vendor/github.com/danwinship/knftables/transaction.go
generated
vendored
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Transaction represents an nftables transaction
|
||||
type Transaction struct {
|
||||
*nftContext
|
||||
|
||||
operations []operation
|
||||
err error
|
||||
}
|
||||
|
||||
// operation contains a single nftables operation (eg "add table", "flush chain")
|
||||
type operation struct {
|
||||
verb verb
|
||||
obj Object
|
||||
}
|
||||
|
||||
// verb is used internally to represent the different "nft" verbs
|
||||
type verb string
|
||||
|
||||
const (
|
||||
addVerb verb = "add"
|
||||
createVerb verb = "create"
|
||||
insertVerb verb = "insert"
|
||||
replaceVerb verb = "replace"
|
||||
deleteVerb verb = "delete"
|
||||
flushVerb verb = "flush"
|
||||
)
|
||||
|
||||
// asCommandBuf returns the transaction as an io.Reader that outputs a series of nft commands
|
||||
func (tx *Transaction) asCommandBuf() (io.Reader, error) {
|
||||
if tx.err != nil {
|
||||
return nil, tx.err
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for _, op := range tx.operations {
|
||||
op.obj.writeOperation(op.verb, tx.nftContext, buf)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// String returns the transaction as a string containing the nft commands; if there is
|
||||
// a pending error, it will be output as a comment at the end of the transaction.
|
||||
func (tx *Transaction) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
for _, op := range tx.operations {
|
||||
op.obj.writeOperation(op.verb, tx.nftContext, buf)
|
||||
}
|
||||
|
||||
if tx.err != nil {
|
||||
fmt.Fprintf(buf, "# ERROR: %v", tx.err)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (tx *Transaction) operation(verb verb, obj Object) {
|
||||
if tx.err != nil {
|
||||
return
|
||||
}
|
||||
if tx.err = obj.validate(verb); tx.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tx.operations = append(tx.operations, operation{verb: verb, obj: obj})
|
||||
}
|
||||
|
||||
// Add adds an "nft add" operation to tx, ensuring that obj exists by creating it if it
|
||||
// did not already exist. (If obj is a Rule, it will be appended to the end of its chain,
|
||||
// or else added after the Rule indicated by this rule's Index or Handle.) The Add() call
|
||||
// always succeeds, but if obj is invalid, or inconsistent with the existing nftables
|
||||
// state, then an error will be returned when the transaction is Run.
|
||||
func (tx *Transaction) Add(obj Object) {
|
||||
tx.operation(addVerb, obj)
|
||||
}
|
||||
|
||||
// Create adds an "nft create" operation to tx, creating obj, which must not already
|
||||
// exist. (If obj is a Rule, it will be appended to the end of its chain, or else added
|
||||
// after the Rule indicated by this rule's Index or Handle.) The Create() call always
|
||||
// succeeds, but if obj is invalid, already exists, or is inconsistent with the existing
|
||||
// nftables state, then an error will be returned when the transaction is Run.
|
||||
func (tx *Transaction) Create(obj Object) {
|
||||
tx.operation(createVerb, obj)
|
||||
}
|
||||
|
||||
// Insert adds an "nft insert" operation to tx, inserting obj (which must be a Rule) at
|
||||
// the start of its chain, or before the other Rule indicated by this rule's Index or
|
||||
// Handle. The Insert() call always succeeds, but if obj is invalid or is inconsistent
|
||||
// with the existing nftables state, then an error will be returned when the transaction
|
||||
// is Run.
|
||||
func (tx *Transaction) Insert(obj Object) {
|
||||
tx.operation(insertVerb, obj)
|
||||
}
|
||||
|
||||
// Replace adds an "nft replace" operation to tx, replacing an existing rule with obj
|
||||
// (which must be a Rule). The Replace() call always succeeds, but if obj is invalid, does
|
||||
// not contain the Handle of an existing rule, or is inconsistent with the existing
|
||||
// nftables state, then an error will be returned when the transaction is Run.
|
||||
func (tx *Transaction) Replace(obj Object) {
|
||||
tx.operation(replaceVerb, obj)
|
||||
}
|
||||
|
||||
// Flush adds an "nft flush" operation to tx, clearing the contents of obj. The Flush()
|
||||
// call always succeeds, but if obj does not exist (or does not support flushing) then an
|
||||
// error will be returned when the transaction is Run.
|
||||
func (tx *Transaction) Flush(obj Object) {
|
||||
tx.operation(flushVerb, obj)
|
||||
}
|
||||
|
||||
// Delete adds an "nft delete" operation to tx, deleting obj. The Delete() call always
|
||||
// succeeds, but if obj does not exist or cannot be deleted based on the information
|
||||
// provided (eg, Handle is required but not set) then an error will be returned when the
|
||||
// transaction is Run.
|
||||
func (tx *Transaction) Delete(obj Object) {
|
||||
tx.operation(deleteVerb, obj)
|
||||
}
|
338
vendor/github.com/danwinship/knftables/types.go
generated
vendored
338
vendor/github.com/danwinship/knftables/types.go
generated
vendored
@@ -1,338 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Maximum length of a table, chain, set, etc, name
|
||||
NameLengthMax = 256
|
||||
|
||||
// Maximum length of a comment
|
||||
CommentLengthMax = 128
|
||||
)
|
||||
|
||||
// Object is the interface for an nftables object. All of the concrete object types
|
||||
// implement this interface.
|
||||
type Object interface {
|
||||
// validate validates an object for an operation
|
||||
validate(verb verb) error
|
||||
|
||||
// writeOperation writes out an "nft" operation involving the object. It assumes
|
||||
// that the object has been validated.
|
||||
writeOperation(verb verb, ctx *nftContext, writer io.Writer)
|
||||
}
|
||||
|
||||
// Family is an nftables family
|
||||
type Family string
|
||||
|
||||
const (
|
||||
// IPv4Family represents the "ip" nftables family, for IPv4 rules.
|
||||
IPv4Family Family = "ip"
|
||||
|
||||
// IPv6Family represents the "ip6" nftables family, for IPv6 rules.
|
||||
IPv6Family Family = "ip6"
|
||||
|
||||
// InetFamily represents the "inet" nftables family, for mixed IPv4 and IPv6 rules.
|
||||
InetFamily Family = "inet"
|
||||
|
||||
// ARPFamily represents the "arp" nftables family, for ARP rules.
|
||||
ARPFamily Family = "arp"
|
||||
|
||||
// BridgeFamily represents the "bridge" nftables family, for rules operating
|
||||
// on packets traversing a bridge.
|
||||
BridgeFamily Family = "bridge"
|
||||
|
||||
// NetDevFamily represents the "netdev" nftables family, for rules operating on
|
||||
// the device ingress/egress path.
|
||||
NetDevFamily Family = "netdev"
|
||||
)
|
||||
|
||||
// Table represents an nftables table.
|
||||
type Table struct {
|
||||
// Comment is an optional comment for the table. (Note that this can be specified
|
||||
// on creation, but depending on the version of /sbin/nft that is available, it
|
||||
// may not be filled in correctly in the result of a List.)
|
||||
Comment *string
|
||||
|
||||
// Handle is an identifier that can be used to uniquely identify an object when
|
||||
// deleting it. When adding a new object, this must be nil.
|
||||
Handle *int
|
||||
}
|
||||
|
||||
// BaseChainType represents the "type" of a "base chain" (ie, a chain that is attached to a hook)
|
||||
type BaseChainType string
|
||||
|
||||
const (
|
||||
// FilterType is the chain type for basic packet filtering.
|
||||
FilterType BaseChainType = "filter"
|
||||
|
||||
// NATType is the chain type for doing DNAT, SNAT, and masquerading.
|
||||
// NAT operations are only available from certain hooks.
|
||||
NATType BaseChainType = "nat"
|
||||
|
||||
// RouteType is the chain type for rules that change the routing of packets.
|
||||
// Chains of this type can only be added to the "output" hook.
|
||||
RouteType BaseChainType = "route"
|
||||
)
|
||||
|
||||
// BaseChainHook represents the "hook" that a base chain is attached to
|
||||
type BaseChainHook string
|
||||
|
||||
// FIXME: document these correctly; virtually all of the existing iptables/nftables
|
||||
// documentation is slightly wrong, particular wrt locally-generated packets.
|
||||
const (
|
||||
PreroutingHook BaseChainHook = "prerouting"
|
||||
InputHook BaseChainHook = "input"
|
||||
ForwardHook BaseChainHook = "forward"
|
||||
OutputHook BaseChainHook = "output"
|
||||
PostroutingHook BaseChainHook = "postrouting"
|
||||
IngressHook BaseChainHook = "ingress"
|
||||
EgressHook BaseChainHook = "egress"
|
||||
)
|
||||
|
||||
// BaseChainPriority represents the "priority" of a base chain. In addition to the const
|
||||
// values, you can also use a signed integer value, or an arithmetic expression consisting
|
||||
// of a const value followed by "+" or "-" and an integer. Lower values run earlier.
|
||||
type BaseChainPriority string
|
||||
|
||||
const (
|
||||
// RawPriority is the earliest named priority. In particular, it can be used for
|
||||
// rules that need to run before conntrack. It is equivalent to the value -300 and
|
||||
// can be used in the ip, ip6, and inet families.
|
||||
RawPriority BaseChainPriority = "raw"
|
||||
|
||||
// ManglePriority is the standard priority for packet-rewriting operations. It is
|
||||
// equivalent to the value -150 and can be used in the ip, ip6, and inet families.
|
||||
ManglePriority BaseChainPriority = "mangle"
|
||||
|
||||
// DNATPriority is the standard priority for DNAT operations. In the ip, ip6, and
|
||||
// inet families, it is equivalent to the value -100. In the bridge family it is
|
||||
// equivalent to the value -300. In both cases it can only be used from the
|
||||
// prerouting hook.
|
||||
DNATPriority BaseChainPriority = "dstnat"
|
||||
|
||||
// FilterPriority is the standard priority for filtering operations. In the ip,
|
||||
// ip6, inet, arp, and netdev families, it is equivalent to the value 0. In the
|
||||
// bridge family it is equivalent to the value -200.
|
||||
FilterPriority BaseChainPriority = "filter"
|
||||
|
||||
// OutPriority is FIXME. It is equivalent to the value 300 and can only be used in
|
||||
// the bridge family.
|
||||
OutPriority BaseChainPriority = "out"
|
||||
|
||||
// SecurityPriority is the standard priority for security operations ("where
|
||||
// secmark can be set for example"). It is equivalent to the value 50 and can be
|
||||
// used in the ip, ip6, and inet families.
|
||||
SecurityPriority BaseChainPriority = "security"
|
||||
|
||||
// SNATPriority is the standard priority for SNAT operations. In the ip, ip6, and
|
||||
// inet families, it is equivalent to the value 100. In the bridge family it is
|
||||
// equivalent to the value 300. In both cases it can only be used from the
|
||||
// postrouting hook.
|
||||
SNATPriority BaseChainPriority = "srcnat"
|
||||
)
|
||||
|
||||
// Chain represents an nftables chain; either a "base chain" (if Type, Hook, and Priority
|
||||
// are specified), or a "regular chain" (if they are not).
|
||||
type Chain struct {
|
||||
// Name is the name of the chain.
|
||||
Name string
|
||||
|
||||
// Type is the chain type; this must be set for a base chain and unset for a
|
||||
// regular chain.
|
||||
Type *BaseChainType
|
||||
// Hook is the hook that the chain is connected to; this must be set for a base
|
||||
// chain and unset for a regular chain.
|
||||
Hook *BaseChainHook
|
||||
// Priority is the chain priority; this must be set for a base chain and unset for
|
||||
// a regular chain. You can call ParsePriority() to convert this to a number.
|
||||
Priority *BaseChainPriority
|
||||
|
||||
// Comment is an optional comment for the object.
|
||||
Comment *string
|
||||
|
||||
// Handle is an identifier that can be used to uniquely identify an object when
|
||||
// deleting it. When adding a new object, this must be nil
|
||||
Handle *int
|
||||
}
|
||||
|
||||
// Rule represents a rule in a chain
|
||||
type Rule struct {
|
||||
// Chain is the name of the chain that contains this rule
|
||||
Chain string
|
||||
|
||||
// Rule is the rule in standard nftables syntax. (Should be empty on Delete, but
|
||||
// is ignored if not.) Note that this does not include any rule comment, which is
|
||||
// separate from the rule itself.
|
||||
Rule string
|
||||
|
||||
// Comment is an optional comment for the rule.
|
||||
Comment *string
|
||||
|
||||
// Index is the number of a rule (counting from 0) to Add this Rule after or
|
||||
// Insert it before. Cannot be specified along with Handle. If neither Index
|
||||
// nor Handle is specified then Add appends the rule the end of the chain and
|
||||
// Insert prepends it to the beginning.
|
||||
Index *int
|
||||
|
||||
// Handle is a rule handle. In Add or Insert, if set, this is the handle of
|
||||
// existing rule to put the new rule after/before. In Delete or Replace, this
|
||||
// indicates the existing rule to delete/replace, and is mandatory. In the result
|
||||
// of a List, this will indicate the rule's handle that can then be used in a
|
||||
// later operation.
|
||||
Handle *int
|
||||
}
|
||||
|
||||
// SetFlag represents a set or map flag
|
||||
type SetFlag string
|
||||
|
||||
const (
|
||||
// ConstantFlag is a flag indicating that the set/map is constant. FIXME UNDOCUMENTED
|
||||
ConstantFlag SetFlag = "constant"
|
||||
|
||||
// DynamicFlag is a flag indicating that the set contains stateful objects
|
||||
// (counters, quotas, or limits) that will be dynamically updated.
|
||||
DynamicFlag SetFlag = "dynamic"
|
||||
|
||||
// IntervalFlag is a flag indicating that the set contains either CIDR elements or
|
||||
// IP ranges.
|
||||
IntervalFlag SetFlag = "interval"
|
||||
|
||||
// TimeoutFlag is a flag indicating that the set/map has a timeout after which
|
||||
// dynamically added elements will be removed. (It is set automatically if the
|
||||
// set/map has a Timeout.)
|
||||
TimeoutFlag SetFlag = "timeout"
|
||||
)
|
||||
|
||||
// SetPolicy represents a set or map storage policy
|
||||
type SetPolicy string
|
||||
|
||||
const (
|
||||
// PolicyPerformance FIXME
|
||||
PerformancePolicy SetPolicy = "performance"
|
||||
|
||||
// PolicyMemory FIXME
|
||||
MemoryPolicy SetPolicy = "memory"
|
||||
)
|
||||
|
||||
// Set represents the definition of an nftables set (but not its elements)
|
||||
type Set struct {
|
||||
// Name is the name of the set.
|
||||
Name string
|
||||
|
||||
// Type is the type of the set key (eg "ipv4_addr"). Either Type or TypeOf, but
|
||||
// not both, must be non-empty.
|
||||
Type string
|
||||
|
||||
// TypeOf is the type of the set key as an nftables expression (eg "ip saddr").
|
||||
// Either Type or TypeOf, but not both, must be non-empty.
|
||||
TypeOf string
|
||||
|
||||
// Flags are the set flags
|
||||
Flags []SetFlag
|
||||
|
||||
// Timeout is the time that an element will stay in the set before being removed.
|
||||
// (Optional; mandatory for sets that will be added to from the packet path)
|
||||
Timeout *time.Duration
|
||||
|
||||
// GCInterval is the interval at which timed-out elements will be removed from the
|
||||
// set. (Optional; FIXME DEFAULT)
|
||||
GCInterval *time.Duration
|
||||
|
||||
// Size if the maximum numer of elements in the set.
|
||||
// (Optional; mandatory for sets that will be added to from the packet path)
|
||||
Size *uint64
|
||||
|
||||
// Policy is the FIXME
|
||||
Policy *SetPolicy
|
||||
|
||||
// AutoMerge indicates that adjacent/overlapping set elements should be merged
|
||||
// together (only for interval sets)
|
||||
AutoMerge *bool
|
||||
|
||||
// Comment is an optional comment for the object.
|
||||
Comment *string
|
||||
|
||||
// Handle is an identifier that can be used to uniquely identify an object when
|
||||
// deleting it. When adding a new object, this must be nil
|
||||
Handle *int
|
||||
}
|
||||
|
||||
// Map represents the definition of an nftables map (but not its elements)
|
||||
type Map struct {
|
||||
// Name is the name of the map.
|
||||
Name string
|
||||
|
||||
// Type is the type of the map key and value (eg "ipv4_addr : verdict"). Either
|
||||
// Type or TypeOf, but not both, must be non-empty.
|
||||
Type string
|
||||
|
||||
// TypeOf is the type of the set key as an nftables expression (eg "ip saddr : verdict").
|
||||
// Either Type or TypeOf, but not both, must be non-empty.
|
||||
TypeOf string
|
||||
|
||||
// Flags are the map flags
|
||||
Flags []SetFlag
|
||||
|
||||
// Timeout is the time that an element will stay in the set before being removed.
|
||||
// (Optional; mandatory for sets that will be added to from the packet path)
|
||||
Timeout *time.Duration
|
||||
|
||||
// GCInterval is the interval at which timed-out elements will be removed from the
|
||||
// set. (Optional; FIXME DEFAULT)
|
||||
GCInterval *time.Duration
|
||||
|
||||
// Size if the maximum numer of elements in the set.
|
||||
// (Optional; mandatory for sets that will be added to from the packet path)
|
||||
Size *uint64
|
||||
|
||||
// Policy is the FIXME
|
||||
Policy *SetPolicy
|
||||
|
||||
// Comment is an optional comment for the object.
|
||||
Comment *string
|
||||
|
||||
// Handle is an identifier that can be used to uniquely identify an object when
|
||||
// deleting it. When adding a new object, this must be nil
|
||||
Handle *int
|
||||
}
|
||||
|
||||
// Element represents a set or map element
|
||||
type Element struct {
|
||||
// Set is the name of the set that contains this element (or the empty string if
|
||||
// this is a map element.)
|
||||
Set string
|
||||
|
||||
// Map is the name of the map that contains this element (or the empty string if
|
||||
// this is a set element.)
|
||||
Map string
|
||||
|
||||
// Key is the element key. (The list contains a single element for "simple" keys,
|
||||
// or multiple elements for concatenations.)
|
||||
Key []string
|
||||
|
||||
// Value is the map element value. As with Key, this may be a single value or
|
||||
// multiple. For set elements, this must be nil.
|
||||
Value []string
|
||||
|
||||
// Comment is an optional comment for the element
|
||||
Comment *string
|
||||
}
|
117
vendor/github.com/danwinship/knftables/util.go
generated
vendored
117
vendor/github.com/danwinship/knftables/util.go
generated
vendored
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Red Hat, 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.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PtrTo can be used to fill in optional field values in objects
|
||||
func PtrTo[T any](val T) *T {
|
||||
return &val
|
||||
}
|
||||
|
||||
var numericPriorities = map[string]int{
|
||||
"raw": -300,
|
||||
"mangle": -150,
|
||||
"dstnat": -100,
|
||||
"filter": 0,
|
||||
"security": 50,
|
||||
"srcnat": 100,
|
||||
}
|
||||
|
||||
var bridgeNumericPriorities = map[string]int{
|
||||
"dstnat": -300,
|
||||
"filter": -200,
|
||||
"out": 100,
|
||||
"srcnat": 300,
|
||||
}
|
||||
|
||||
// ParsePriority tries to convert the string form of a chain priority into a number
|
||||
func ParsePriority(family Family, priority string) (int, error) {
|
||||
val, err := strconv.Atoi(priority)
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
modVal := 0
|
||||
if i := strings.IndexAny(priority, "+-"); i != -1 {
|
||||
mod := priority[i:]
|
||||
modVal, err = strconv.Atoi(mod)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("could not parse modifier %q: %w", mod, err)
|
||||
}
|
||||
priority = priority[:i]
|
||||
}
|
||||
|
||||
var found bool
|
||||
if family == BridgeFamily {
|
||||
val, found = bridgeNumericPriorities[priority]
|
||||
} else {
|
||||
val, found = numericPriorities[priority]
|
||||
}
|
||||
if !found {
|
||||
return 0, fmt.Errorf("unknown priority %q", priority)
|
||||
}
|
||||
|
||||
return val + modVal, nil
|
||||
}
|
||||
|
||||
// Concat is a helper (primarily) for constructing Rule objects. It takes a series of
|
||||
// arguments and concatenates them together into a single string with spaces between the
|
||||
// arguments. Strings are output as-is, string arrays are output element by element,
|
||||
// numbers are output as with `fmt.Sprintf("%d")`, and all other types are output as with
|
||||
// `fmt.Sprintf("%s")`. To help with set/map lookup syntax, an argument of "@" will not
|
||||
// be followed by a space, so you can do, eg, `Concat("ip saddr", "@", setName)`.
|
||||
func Concat(args ...interface{}) string {
|
||||
b := &strings.Builder{}
|
||||
var needSpace, wroteAt bool
|
||||
for _, arg := range args {
|
||||
switch x := arg.(type) {
|
||||
case string:
|
||||
if needSpace {
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
b.WriteString(x)
|
||||
wroteAt = (x == "@")
|
||||
case []string:
|
||||
for _, s := range x {
|
||||
if needSpace {
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
b.WriteString(s)
|
||||
wroteAt = (s == "@")
|
||||
needSpace = b.Len() > 0 && !wroteAt
|
||||
}
|
||||
case int, uint, int16, uint16, int32, uint32, int64, uint64:
|
||||
if needSpace {
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
fmt.Fprintf(b, "%d", x)
|
||||
default:
|
||||
if needSpace {
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
fmt.Fprintf(b, "%s", x)
|
||||
}
|
||||
|
||||
needSpace = b.Len() > 0 && !wroteAt
|
||||
}
|
||||
return b.String()
|
||||
}
|
Reference in New Issue
Block a user