diff --git a/go.mod b/go.mod index 96e8c10de..371cf5dbf 100644 --- a/go.mod +++ b/go.mod @@ -11,17 +11,17 @@ require ( github.com/coreos/go-systemd/v22 v22.7.0 github.com/godbus/dbus/v5 v5.2.2 github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 - github.com/mattn/go-shellwords v1.0.13 + github.com/mattn/go-shellwords v1.0.14 github.com/networkplumbing/go-nft v0.4.0 github.com/onsi/ginkgo/v2 v2.32.0 - github.com/onsi/gomega v1.40.0 + github.com/onsi/gomega v1.42.1 github.com/opencontainers/selinux v1.15.1 github.com/pkg/errors v0.9.1 github.com/safchain/ethtool v0.7.0 github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netns v0.0.5 - golang.org/x/sys v0.45.0 - sigs.k8s.io/knftables v0.0.21 + golang.org/x/sys v0.47.0 + sigs.k8s.io/knftables v0.0.22 ) require ( @@ -38,20 +38,22 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/nftables v0.3.0 // indirect github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/josharian/native v1.1.0 // indirect + github.com/mdlayher/netlink v1.11.2 // indirect github.com/mdlayher/packet v1.1.2 // indirect - github.com/mdlayher/socket v0.5.1 // indirect + github.com/mdlayher/socket v0.6.1 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect go.opencensus.io v0.24.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.35.0 // indirect - golang.org/x/net v0.55.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/text v0.37.0 // indirect - golang.org/x/tools v0.44.0 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/text v0.40.0 // indirect + golang.org/x/tools v0.47.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/grpc v1.79.3 // indirect google.golang.org/protobuf v1.36.10 // indirect diff --git a/go.sum b/go.sum index 000415c9c..9b64f0301 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/nftables v0.3.0 h1:bkyZ0cbpVeMHXOrtlFc8ISmfVqq5gPJukoYieyVmITg= +github.com/google/nftables v0.3.0/go.mod h1:BCp9FsrbF1Fn/Yu6CLUc9GGZFw/+hsxfluNXXmxBfRM= github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -99,20 +101,22 @@ github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffkt github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= -github.com/mattn/go-shellwords v1.0.13 h1:DC0OMEpGjm6LfNFU4ckYcvbQKyp2vE8atyFGXNtDcf4= -github.com/mattn/go-shellwords v1.0.13/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-shellwords v1.0.14 h1:yUKzIgsCnosndOASY6/enly1EAuaXeFSQ7cdyA3OuYg= +github.com/mattn/go-shellwords v1.0.14/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mdlayher/netlink v1.11.2 h1:HKh2jqe+omdSWcQ88nrT7INE61B0NXfiSPFdgL4YbNI= +github.com/mdlayher/netlink v1.11.2/go.mod h1:uT2Yc/QLaZubzDpZIBi9d4GoeLwtp3x1AMeqSRrK2sA= github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY= github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= -github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= -github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A= +github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/networkplumbing/go-nft v0.4.0 h1:kExVMwXW48DOAukkBwyI16h4uhE5lN9iMvQd52lpTyU= github.com/networkplumbing/go-nft v0.4.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= -github.com/onsi/gomega v1.40.0 h1:Vtol0e1MghCD2ZVIilPDIg44XSL9l2QAn8ZNaljWcJc= -github.com/onsi/gomega v1.40.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= +github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= +github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= github.com/opencontainers/selinux v1.15.1 h1:ERxeh5caJvCzNAKdI8WQbJmB1LDTn4BuaAg8wihLBpA= github.com/opencontainers/selinux v1.15.1/go.mod h1:LenyElirjUHszfxrjuFqC85HIeXZKumHcKMQtnaDlQQ= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= @@ -169,8 +173,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -181,8 +185,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -190,8 +194,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -203,13 +207,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -219,8 +223,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -250,14 +254,13 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -sigs.k8s.io/knftables v0.0.21 h1:mby+JtzhJH1Ucnq++ZJaM+ViQBY5JzIyX4bSCP8K5YQ= -sigs.k8s.io/knftables v0.0.21/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= +sigs.k8s.io/knftables v0.0.22 h1:RMbBSxQb4t730I6m492NDzWTOaActPpF9OCACQmF9bE= +sigs.k8s.io/knftables v0.0.22/go.mod h1:tig4GDnk1aG8sNsKW/iqid/PGgVLxJLEZkuUlO5C44Y= diff --git a/vendor/github.com/google/nftables/CONTRIBUTING.md b/vendor/github.com/google/nftables/CONTRIBUTING.md new file mode 100644 index 000000000..ae319c70a --- /dev/null +++ b/vendor/github.com/google/nftables/CONTRIBUTING.md @@ -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 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. diff --git a/vendor/github.com/google/nftables/LICENSE b/vendor/github.com/google/nftables/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/google/nftables/LICENSE @@ -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. diff --git a/vendor/github.com/google/nftables/README.md b/vendor/github.com/google/nftables/README.md new file mode 100644 index 000000000..cb633c718 --- /dev/null +++ b/vendor/github.com/google/nftables/README.md @@ -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! + + diff --git a/vendor/github.com/google/nftables/alignedbuff/alignedbuff.go b/vendor/github.com/google/nftables/alignedbuff/alignedbuff.go new file mode 100644 index 000000000..a97214649 --- /dev/null +++ b/vendor/github.com/google/nftables/alignedbuff/alignedbuff.go @@ -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)) diff --git a/vendor/github.com/google/nftables/binaryutil/binaryutil.go b/vendor/github.com/google/nftables/binaryutil/binaryutil.go new file mode 100644 index 000000000..e61973f07 --- /dev/null +++ b/vendor/github.com/google/nftables/binaryutil/binaryutil.go @@ -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")) +} diff --git a/vendor/github.com/google/nftables/chain.go b/vendor/github.com/google/nftables/chain.go new file mode 100644 index 000000000..4f4c0a532 --- /dev/null +++ b/vendor/github.com/google/nftables/chain.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/compat_policy.go b/vendor/github.com/google/nftables/compat_policy.go new file mode 100644 index 000000000..c1f390855 --- /dev/null +++ b/vendor/github.com/google/nftables/compat_policy.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/conn.go b/vendor/github.com/google/nftables/conn.go new file mode 100644 index 000000000..fef9c2a4e --- /dev/null +++ b/vendor/github.com/google/nftables/conn.go @@ -0,0 +1,371 @@ +// 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" + "syscall" + + "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. + sockOptions []SockOption +} + +// ConnOption is an option to change the behavior of the nftables Conn returned by Open. +type ConnOption func(*Conn) + +// SockOption is an option to change the behavior of the netlink socket used by the nftables Conn. +type SockOption func(*netlink.Conn) error + +// 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 + } +} + +// WithSockOptions sets the specified socket options when creating a new netlink +// connection. +func WithSockOptions(opts ...SockOption) ConnOption { + return func(cc *Conn) { + cc.sockOptions = append(cc.sockOptions, opts...) + } +} + +// 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) || errors.Is(err, syscall.ENOBUFS) { + // Kernel will only send one 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) { + var ( + conn *netlink.Conn + err error + ) + + if cc.TestDial != nil { + conn = nltest.Dial(cc.TestDial) + } else { + conn, err = netlink.Dial(unix.NETLINK_NETFILTER, &netlink.Config{NetNS: cc.NetNS}) + } + + if err != nil { + return nil, err + } + + for _, opt := range cc.sockOptions { + if err := opt(conn); err != nil { + return nil, err + } + } + + return conn, nil +} + +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 +} diff --git a/vendor/github.com/google/nftables/counter.go b/vendor/github.com/google/nftables/counter.go new file mode 100644 index 000000000..d18fc49c5 --- /dev/null +++ b/vendor/github.com/google/nftables/counter.go @@ -0,0 +1,63 @@ +// 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/expr" + "github.com/mdlayher/netlink" + "golang.org/x/sys/unix" +) + +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) data() expr.Any { + return &expr.Counter{ + Bytes: c.Bytes, + Packets: c.Packets, + } +} + +func (c *CounterObj) name() string { + return c.Name +} +func (c *CounterObj) objType() ObjType { + return ObjTypeCounter +} + +func (c *CounterObj) table() *Table { + return c.Table +} + +func (c *CounterObj) family() TableFamily { + return c.Table.Family +} diff --git a/vendor/github.com/google/nftables/doc.go b/vendor/github.com/google/nftables/doc.go new file mode 100644 index 000000000..41985b35e --- /dev/null +++ b/vendor/github.com/google/nftables/doc.go @@ -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 diff --git a/vendor/github.com/google/nftables/expr/bitwise.go b/vendor/github.com/google/nftables/expr/bitwise.go new file mode 100644 index 000000000..5f3cdeac7 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/bitwise.go @@ -0,0 +1,106 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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 + } + + return 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}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/byteorder.go b/vendor/github.com/google/nftables/expr/byteorder.go new file mode 100644 index 000000000..cf9e2fe58 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/byteorder.go @@ -0,0 +1,63 @@ +// 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 := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + return 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)}, + }) +} + +func (e *Byteorder) unmarshal(fam byte, data []byte) error { + return fmt.Errorf("not yet implemented") +} diff --git a/vendor/github.com/google/nftables/expr/connlimit.go b/vendor/github.com/google/nftables/expr/connlimit.go new file mode 100644 index 000000000..11bd07bf4 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/connlimit.go @@ -0,0 +1,74 @@ +// 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 := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + return netlink.MarshalAttributes([]netlink.Attribute{ + {Type: NFTA_CONNLIMIT_COUNT, Data: binaryutil.BigEndian.PutUint32(e.Count)}, + {Type: NFTA_CONNLIMIT_FLAGS, Data: binaryutil.BigEndian.PutUint32(e.Flags)}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/counter.go b/vendor/github.com/google/nftables/expr/counter.go new file mode 100644 index 000000000..7483ee45b --- /dev/null +++ b/vendor/github.com/google/nftables/expr/counter.go @@ -0,0 +1,64 @@ +// 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 := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + return 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)}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/ct.go b/vendor/github.com/google/nftables/expr/ct.go new file mode 100644 index 000000000..127b6fdd0 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/ct.go @@ -0,0 +1,400 @@ +// 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 +) + +// Missing ct timeout consts +// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=be0bae0ad31b0adb506f96de083f52a2bd0d4fbf#n1592 +const ( + NFTA_CT_TIMEOUT_L3PROTO = 0x01 + NFTA_CT_TIMEOUT_L4PROTO = 0x02 + NFTA_CT_TIMEOUT_DATA = 0x03 +) + +type CtStatePolicyTimeout map[uint16]uint32 + +const ( + // https://git.netfilter.org/libnftnl/tree/src/obj/ct_timeout.c?id=116e95aa7b6358c917de8c69f6f173874030b46b#n24 + CtStateTCPSYNSENT = iota + CtStateTCPSYNRECV + CtStateTCPESTABLISHED + CtStateTCPFINWAIT + CtStateTCPCLOSEWAIT + CtStateTCPLASTACK + CtStateTCPTIMEWAIT + CtStateTCPCLOSE + CtStateTCPSYNSENT2 + CtStateTCPRETRANS + CtStateTCPUNACK +) + +// https://git.netfilter.org/libnftnl/tree/src/obj/ct_timeout.c?id=116e95aa7b6358c917de8c69f6f173874030b46b#n38 +var CtStateTCPTimeoutDefaults CtStatePolicyTimeout = map[uint16]uint32{ + CtStateTCPSYNSENT: 120, + CtStateTCPSYNRECV: 60, + CtStateTCPESTABLISHED: 43200, + CtStateTCPFINWAIT: 120, + CtStateTCPCLOSEWAIT: 60, + CtStateTCPLASTACK: 30, + CtStateTCPTIMEWAIT: 120, + CtStateTCPCLOSE: 10, + CtStateTCPSYNSENT2: 120, + CtStateTCPRETRANS: 300, + CtStateTCPUNACK: 300, +} + +const ( + // https://git.netfilter.org/libnftnl/tree/src/obj/ct_timeout.c?id=116e95aa7b6358c917de8c69f6f173874030b46b#n57 + CtStateUDPUNREPLIED = iota + CtStateUDPREPLIED +) + +// https://git.netfilter.org/libnftnl/tree/src/obj/ct_timeout.c?id=116e95aa7b6358c917de8c69f6f173874030b46b#n57 +var CtStateUDPTimeoutDefaults CtStatePolicyTimeout = map[uint16]uint32{ + CtStateUDPUNREPLIED: 30, + CtStateUDPREPLIED: 180, +} + +// Ct defines type for NFT connection tracking +type Ct struct { + Register uint32 + SourceRegister bool + Key CtKey + Direction uint32 +} + +func (e *Ct) marshal(fam byte) ([]byte, error) { + exprData, err := e.marshalData(fam) + if err != nil { + return nil, err + } + + 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) marshalData(fam byte) ([]byte, error) { + var 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...) + + switch e.Key { + case CtKeySRC, CtKeyDST, CtKeyPROTOSRC, CtKeyPROTODST: + regData, err = netlink.MarshalAttributes( + []netlink.Attribute{ + {Type: unix.NFTA_CT_DIRECTION, Data: binaryutil.BigEndian.PutUint32(e.Direction)}, + }, + ) + if err != nil { + return nil, err + } + exprData = append(exprData, regData...) + } + + return exprData, nil +} + +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() + case unix.NFTA_CT_DIRECTION: + e.Direction = ad.Uint32() + } + } + return ad.Err() +} + +type CtHelper struct { + Name string + L3Proto uint16 + L4Proto uint8 +} + +func (c *CtHelper) marshal(fam byte) ([]byte, error) { + exprData, err := c.marshalData(fam) + if err != nil { + return nil, err + } + + return netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_EXPR_NAME, Data: []byte("cthelper\x00")}, + {Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: exprData}, + }) +} + +func (c *CtHelper) marshalData(fam byte) ([]byte, error) { + exprData := []netlink.Attribute{ + {Type: unix.NFTA_CT_HELPER_NAME, Data: []byte(c.Name)}, + } + + if c.L3Proto != 0 { + exprData = append(exprData, netlink.Attribute{ + Type: unix.NFTA_CT_HELPER_L3PROTO, Data: binaryutil.BigEndian.PutUint16(c.L3Proto), + }) + } + if c.L4Proto != 0 { + exprData = append(exprData, netlink.Attribute{ + Type: unix.NFTA_CT_HELPER_L4PROTO, Data: []byte{c.L4Proto}, + }) + } + + return netlink.MarshalAttributes(exprData) +} + +func (c *CtHelper) 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_HELPER_NAME: + c.Name = ad.String() + case unix.NFTA_CT_HELPER_L3PROTO: + c.L3Proto = ad.Uint16() + case unix.NFTA_CT_HELPER_L4PROTO: + c.L4Proto = ad.Uint8() + } + } + return ad.Err() +} + +// From https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=be0bae0ad31b0adb506f96de083f52a2bd0d4fbf#n1601 +// Currently not available in sys/unix +const ( + NFTA_CT_EXPECT_L3PROTO = 0x01 + NFTA_CT_EXPECT_L4PROTO = 0x02 + NFTA_CT_EXPECT_DPORT = 0x03 + NFTA_CT_EXPECT_TIMEOUT = 0x04 + NFTA_CT_EXPECT_SIZE = 0x05 +) + +type CtExpect struct { + L3Proto uint16 + L4Proto uint8 + DPort uint16 + Timeout uint32 + Size uint8 +} + +func (c *CtExpect) marshal(fam byte) ([]byte, error) { + exprData, err := c.marshalData(fam) + if err != nil { + return nil, err + } + + return netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_EXPR_NAME, Data: []byte("ctexpect\x00")}, + {Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: exprData}, + }) +} + +func (c *CtExpect) marshalData(fam byte) ([]byte, error) { + // all elements except l3proto must be defined + // per https://git.netfilter.org/nftables/tree/doc/stateful-objects.txt?id=db70959a5ccf2952b218f51c3d529e186a5a43bb#n119 + // from man page: l3proto is derived from the table family by default + exprData := []netlink.Attribute{ + {Type: NFTA_CT_EXPECT_L4PROTO, Data: []byte{c.L4Proto}}, + {Type: NFTA_CT_EXPECT_DPORT, Data: binaryutil.BigEndian.PutUint16(c.DPort)}, + {Type: NFTA_CT_EXPECT_TIMEOUT, Data: binaryutil.BigEndian.PutUint32(c.Timeout)}, + {Type: NFTA_CT_EXPECT_SIZE, Data: []byte{c.Size}}, + } + + if c.L3Proto != 0 { + attr := netlink.Attribute{Type: NFTA_CT_EXPECT_L3PROTO, Data: binaryutil.BigEndian.PutUint16(c.L3Proto)} + exprData = append(exprData, attr) + } + return netlink.MarshalAttributes(exprData) +} + +func (c *CtExpect) 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_CT_EXPECT_L3PROTO: + c.L3Proto = ad.Uint16() + case NFTA_CT_EXPECT_L4PROTO: + c.L4Proto = ad.Uint8() + case NFTA_CT_EXPECT_DPORT: + c.DPort = ad.Uint16() + case NFTA_CT_EXPECT_TIMEOUT: + c.Timeout = ad.Uint32() + case NFTA_CT_EXPECT_SIZE: + c.Size = ad.Uint8() + } + } + return ad.Err() +} + +type CtTimeout struct { + L3Proto uint16 + L4Proto uint8 + Policy CtStatePolicyTimeout +} + +func (c *CtTimeout) marshal(fam byte) ([]byte, error) { + exprData, err := c.marshalData(fam) + if err != nil { + return nil, err + } + + return netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_EXPR_NAME, Data: []byte("cttimeout\x00")}, + {Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: exprData}, + }) +} + +func (c *CtTimeout) marshalData(fam byte) ([]byte, error) { + var policy CtStatePolicyTimeout + switch c.L4Proto { + case unix.IPPROTO_UDP: + policy = CtStateUDPTimeoutDefaults + default: + policy = CtStateTCPTimeoutDefaults + } + + for k, v := range c.Policy { + policy[k] = v + } + + var policyAttrs []netlink.Attribute + for k, v := range policy { + policyAttrs = append(policyAttrs, netlink.Attribute{Type: k + 1, Data: binaryutil.BigEndian.PutUint32(v)}) + } + policyData, err := netlink.MarshalAttributes(policyAttrs) + if err != nil { + return nil, err + } + + exprData := []netlink.Attribute{ + {Type: NFTA_CT_TIMEOUT_L3PROTO, Data: binaryutil.BigEndian.PutUint16(c.L3Proto)}, + {Type: NFTA_CT_TIMEOUT_L4PROTO, Data: []byte{c.L4Proto}}, + {Type: unix.NLA_F_NESTED | NFTA_CT_TIMEOUT_DATA, Data: policyData}, + } + + return netlink.MarshalAttributes(exprData) +} + +func (c *CtTimeout) 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_CT_TIMEOUT_L3PROTO: + c.L3Proto = ad.Uint16() + case NFTA_CT_TIMEOUT_L4PROTO: + c.L4Proto = ad.Uint8() + case NFTA_CT_TIMEOUT_DATA: + decoder, err := netlink.NewAttributeDecoder(ad.Bytes()) + decoder.ByteOrder = binary.BigEndian + if err != nil { + return err + } + for decoder.Next() { + switch c.L4Proto { + case unix.IPPROTO_UDP: + c.Policy = CtStateUDPTimeoutDefaults + default: + c.Policy = CtStateTCPTimeoutDefaults + } + c.Policy[decoder.Type()-1] = decoder.Uint32() + } + } + } + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/dup.go b/vendor/github.com/google/nftables/expr/dup.go new file mode 100644 index 000000000..9012fdaca --- /dev/null +++ b/vendor/github.com/google/nftables/expr/dup.go @@ -0,0 +1,70 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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)}) + } + + return netlink.MarshalAttributes(attrs) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/dynset.go b/vendor/github.com/google/nftables/expr/dynset.go new file mode 100644 index 000000000..0b3d9bf13 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/dynset.go @@ -0,0 +1,153 @@ +// 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) { + opData, err := e.marshalData(fam) + 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) marshalData(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 { + 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: + flags |= NFT_DYNSET_F_EXPR + 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)}) + return netlink.MarshalAttributes(opAttrs) +} + +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) + 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) + } +} diff --git a/vendor/github.com/google/nftables/expr/expr.go b/vendor/github.com/google/nftables/expr/expr.go new file mode 100644 index 000000000..0a363233b --- /dev/null +++ b/vendor/github.com/google/nftables/expr/expr.go @@ -0,0 +1,502 @@ +// 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() { + parseExprBytesCommonFunc := func(exprsFromBytesFunc func() ([]Any, error)) ([]interface{}, error) { + exprs, err := exprsFromBytesFunc() + if err != nil { + return nil, err + } + result := make([]interface{}, len(exprs)) + for idx, expr := range exprs { + result[idx] = expr + } + return result, nil + } + + parseexprfunc.ParseExprBytesFromNameFunc = func(fam byte, ad *netlink.AttributeDecoder, exprName string) ([]interface{}, error) { + return parseExprBytesCommonFunc(func() ([]Any, error) { + return exprsBytesFromName(fam, ad, exprName) + }) + } + parseexprfunc.ParseExprBytesFunc = func(fam byte, ad *netlink.AttributeDecoder) ([]interface{}, error) { + return parseExprBytesCommonFunc(func() ([]Any, error) { + return exprsFromBytes(fam, ad) + }) + } + 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) + 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) +} + +func MarshalExprData(fam byte, e Any) ([]byte, error) { + return e.marshalData(fam) +} + +// Unmarshal fills an expression from the specified byte slice. +func Unmarshal(fam byte, data []byte, e Any) error { + return e.unmarshal(fam, data) +} + +// exprsBytesFromName parses raw expressions bytes +// based on provided expr name +func exprsBytesFromName(fam byte, ad *netlink.AttributeDecoder, name string) ([]Any, error) { + var exprs []Any + e := exprFromName(name) + ad.Do(func(b []byte) error { + if err := Unmarshal(fam, b, e); err != nil { + return err + } + exprs = append(exprs, e) + return nil + }) + return exprs, ad.Err() +} + +// exprsFromBytes parses nested raw expressions bytes +// to construct nftables expressions +func exprsFromBytes(fam byte, ad *netlink.AttributeDecoder) ([]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: + e := exprFromName(name) + 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() +} + +func exprFromName(name string) Any { + 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{} + case "cthelper": + e = &CtHelper{} + case "synproxy": + e = &SynProxy{} + case "ctexpect": + e = &CtExpect{} + case "secmark": + e = &SecMark{} + case "cttimeout": + e = &CtTimeout{} + case "fib": + e = &Fib{} + case "numgen": + e = &Numgen{} + } + return e +} + +// Any is an interface implemented by any expression type. +type Any interface { + marshal(fam byte) ([]byte, error) + marshalData(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) { + exprData, err := e.marshalData(fam) + if err != nil { + return nil, err + } + + 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) marshalData(fam byte) ([]byte, error) { + var 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 exprData, nil +} + +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 + // NF_NAT_RANGE_PROTO_SPECIFIED defines flag for a specified range + NF_NAT_RANGE_PROTO_SPECIFIED = unix.NF_NAT_RANGE_PROTO_SPECIFIED +) + +func (e *Masq) marshal(fam byte) ([]byte, error) { + msgData, err := e.marshalData(fam) + if err != nil { + return nil, err + } + 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) marshalData(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 msgData, nil +} + +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) { + exprData, err := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + cmpData, err := netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_DATA_VALUE, Data: e.Data}, + }) + if err != nil { + return nil, err + } + return 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}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/exthdr.go b/vendor/github.com/google/nftables/expr/exthdr.go new file mode 100644 index 000000000..0a9d9fcde --- /dev/null +++ b/vendor/github.com/google/nftables/expr/exthdr.go @@ -0,0 +1,106 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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)}) + } + + return netlink.MarshalAttributes(attr) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/fib.go b/vendor/github.com/google/nftables/expr/fib.go new file mode 100644 index 000000000..ea6c059b5 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/fib.go @@ -0,0 +1,140 @@ +// 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, err := e.marshalData(fam) + if err != nil { + return nil, err + } + 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) marshalData(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 data, nil +} + +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() + switch result { + case unix.NFT_FIB_RESULT_OIF: + e.ResultOIF = true + case unix.NFT_FIB_RESULT_OIFNAME: + e.ResultOIFNAME = true + case unix.NFT_FIB_RESULT_ADDRTYPE: + e.ResultADDRTYPE = true + } + case unix.NFTA_FIB_FLAGS: + flags := ad.Uint32() + e.FlagSADDR = (flags & unix.NFTA_FIB_F_SADDR) != 0 + e.FlagDADDR = (flags & unix.NFTA_FIB_F_DADDR) != 0 + e.FlagMARK = (flags & unix.NFTA_FIB_F_MARK) != 0 + e.FlagIIF = (flags & unix.NFTA_FIB_F_IIF) != 0 + e.FlagOIF = (flags & unix.NFTA_FIB_F_OIF) != 0 + e.FlagPRESENT = (flags & unix.NFTA_FIB_F_PRESENT) != 0 + } + } + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/flow_offload.go b/vendor/github.com/google/nftables/expr/flow_offload.go new file mode 100644 index 000000000..de4949a6d --- /dev/null +++ b/vendor/github.com/google/nftables/expr/flow_offload.go @@ -0,0 +1,63 @@ +// 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 := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + return netlink.MarshalAttributes([]netlink.Attribute{ + {Type: NFTNL_EXPR_FLOW_TABLE_NAME, Data: []byte(e.Name)}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/hash.go b/vendor/github.com/google/nftables/expr/hash.go new file mode 100644 index 000000000..92b9eea34 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/hash.go @@ -0,0 +1,98 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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))}, + }...) + return netlink.MarshalAttributes(hashAttrs) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/immediate.go b/vendor/github.com/google/nftables/expr/immediate.go new file mode 100644 index 000000000..19eea4480 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/immediate.go @@ -0,0 +1,83 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + immData, err := netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_DATA_VALUE, Data: e.Data}, + }) + if err != nil { + return nil, err + } + + return 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}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/limit.go b/vendor/github.com/google/nftables/expr/limit.go new file mode 100644 index 000000000..1e170ac31 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/limit.go @@ -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" + "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) { + data, err := l.marshalData(fam) + 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) marshalData(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)}, + } + + return netlink.MarshalAttributes(attrs) +} + +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) != 0 + default: + return errors.New("expr: unhandled limit netlink attribute") + } + } + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/log.go b/vendor/github.com/google/nftables/expr/log.go new file mode 100644 index 000000000..eaa057a63 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/log.go @@ -0,0 +1,154 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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<= /* 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}, + } + return netlink.MarshalAttributes(attrs) +} + +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 +} diff --git a/vendor/github.com/google/nftables/expr/nat.go b/vendor/github.com/google/nftables/expr/nat.go new file mode 100644 index 000000000..3f28967be --- /dev/null +++ b/vendor/github.com/google/nftables/expr/nat.go @@ -0,0 +1,141 @@ +// 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 + Specified 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) { + data, err := e.marshalData(fam) + 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) marshalData(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 e.Specified { + flags |= NF_NAT_RANGE_PROTO_SPECIFIED + } + if flags != 0 { + attrs = append(attrs, netlink.Attribute{Type: unix.NFTA_NAT_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)}) + } + + return netlink.MarshalAttributes(attrs) +} + +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 + e.Specified = (flags & NF_NAT_RANGE_PROTO_SPECIFIED) != 0 + } + } + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/notrack.go b/vendor/github.com/google/nftables/expr/notrack.go new file mode 100644 index 000000000..c17d7b586 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/notrack.go @@ -0,0 +1,42 @@ +// 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) marshalData(fam byte) ([]byte, error) { + return []byte("notrack\x00"), nil +} + +func (e *Notrack) unmarshal(fam byte, data []byte) error { + ad, err := netlink.NewAttributeDecoder(data) + + if err != nil { + return err + } + + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/numgen.go b/vendor/github.com/google/nftables/expr/numgen.go new file mode 100644 index 000000000..362ca0682 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/numgen.go @@ -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" + "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) { + data, err := e.marshalData(fam) + 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) marshalData(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) + } + + return 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)}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/objref.go b/vendor/github.com/google/nftables/expr/objref.go new file mode 100644 index 000000000..e6d59749a --- /dev/null +++ b/vendor/github.com/google/nftables/expr/objref.go @@ -0,0 +1,64 @@ +// 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 := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + return 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?! + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/payload.go b/vendor/github.com/google/nftables/expr/payload.go new file mode 100644 index 000000000..b25128b55 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/payload.go @@ -0,0 +1,134 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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)}, + ) + } + } + + return netlink.MarshalAttributes(attrs) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/queue.go b/vendor/github.com/google/nftables/expr/queue.go new file mode 100644 index 000000000..0e9d8f43e --- /dev/null +++ b/vendor/github.com/google/nftables/expr/queue.go @@ -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" +) + +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) { + data, err := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + if e.Total == 0 { + e.Total = 1 // The total default value is 1 + } + return 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))}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/quota.go b/vendor/github.com/google/nftables/expr/quota.go new file mode 100644 index 000000000..ca55f6c91 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/quota.go @@ -0,0 +1,80 @@ +// 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) { + data, err := q.marshalData(fam) + 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) marshalData(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), + }) + + return netlink.MarshalAttributes(attrs) +} + +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) != 0 + } + } + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/range.go b/vendor/github.com/google/nftables/expr/range.go new file mode 100644 index 000000000..bd6972aaf --- /dev/null +++ b/vendor/github.com/google/nftables/expr/range.go @@ -0,0 +1,132 @@ +// 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) { + data, err := e.marshalData(fam) + if err != nil { + return nil, err + } + + 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) marshalData(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 data, nil +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/redirect.go b/vendor/github.com/google/nftables/expr/redirect.go new file mode 100644 index 000000000..ea535f3c7 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/redirect.go @@ -0,0 +1,75 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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)}) + } + + return netlink.MarshalAttributes(attrs) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/reject.go b/vendor/github.com/google/nftables/expr/reject.go new file mode 100644 index 000000000..7fe216db2 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/reject.go @@ -0,0 +1,63 @@ +// 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 := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + return 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}}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/rt.go b/vendor/github.com/google/nftables/expr/rt.go new file mode 100644 index 000000000..21c3a637e --- /dev/null +++ b/vendor/github.com/google/nftables/expr/rt.go @@ -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 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 := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + return 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)}, + }) +} + +func (e *Rt) unmarshal(fam byte, data []byte) error { + return fmt.Errorf("not yet implemented") +} diff --git a/vendor/github.com/google/nftables/expr/secmark.go b/vendor/github.com/google/nftables/expr/secmark.go new file mode 100644 index 000000000..3faf87f5c --- /dev/null +++ b/vendor/github.com/google/nftables/expr/secmark.go @@ -0,0 +1,64 @@ +// Copyright 2024 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" +) + +// From https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=be0bae0ad31b0adb506f96de083f52a2bd0d4fbf#n1338 +const ( + NFTA_SECMARK_CTX = 0x01 +) + +type SecMark struct { + Ctx string +} + +func (e *SecMark) marshal(fam byte) ([]byte, error) { + data, err := e.marshalData(fam) + if err != nil { + return nil, err + } + return netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_EXPR_NAME, Data: []byte("secmark\x00")}, + {Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data}, + }) +} + +func (e *SecMark) marshalData(fam byte) ([]byte, error) { + attrs := []netlink.Attribute{ + {Type: NFTA_SECMARK_CTX, Data: []byte(e.Ctx)}, + } + return netlink.MarshalAttributes(attrs) +} + +func (e *SecMark) 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_SECMARK_CTX: + e.Ctx = ad.String() + } + } + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/socket.go b/vendor/github.com/google/nftables/expr/socket.go new file mode 100644 index 000000000..e3843ccde --- /dev/null +++ b/vendor/github.com/google/nftables/expr/socket.go @@ -0,0 +1,92 @@ +// 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) { + exprData, err := e.marshalData(fam) + 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) marshalData(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 + return 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))}, + }, + ) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/synproxy.go b/vendor/github.com/google/nftables/expr/synproxy.go new file mode 100644 index 000000000..a93b8bb21 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/synproxy.go @@ -0,0 +1,118 @@ +// Copyright 2024 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 SynProxy struct { + Mss uint16 + Wscale uint8 + Timestamp bool + SackPerm bool + // Probably not expected to be set by users + // https://github.com/torvalds/linux/blob/521b1e7f4cf0b05a47995b103596978224b380a8/net/netfilter/nft_synproxy.c#L30-L31 + Ecn bool + // True when Mss is set to a value or if 0 is an intended value of Mss + MssValueSet bool + // True when Wscale is set to a value or if 0 is an intended value of Wscale + WscaleValueSet bool +} + +// From https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=be0bae0ad31b0adb506f96de083f52a2bd0d4fbf#n1723 +// Currently not available in golang.org/x/sys/unix +const ( + NFTA_SYNPROXY_MSS = 0x01 + NFTA_SYNPROXY_WSCALE = 0x02 + NFTA_SYNPROXY_FLAGS = 0x03 +) + +// From https://github.com/torvalds/linux/blob/521b1e7f4cf0b05a47995b103596978224b380a8/include/uapi/linux/netfilter/nf_synproxy.h#L7-L15 +// Currently not available in golang.org/x/sys/unix +const ( + NF_SYNPROXY_OPT_MSS = 0x01 + NF_SYNPROXY_OPT_WSCALE = 0x02 + NF_SYNPROXY_OPT_SACK_PERM = 0x04 + NF_SYNPROXY_OPT_TIMESTAMP = 0x08 + NF_SYNPROXY_OPT_ECN = 0x10 +) + +func (e *SynProxy) marshal(fam byte) ([]byte, error) { + data, err := e.marshalData(fam) + if err != nil { + return nil, err + } + return netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_EXPR_NAME, Data: []byte("synproxy\x00")}, + {Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data}, + }) +} + +func (e *SynProxy) marshalData(fam byte) ([]byte, error) { + var flags uint32 + if e.Mss != 0 || e.MssValueSet { + flags |= NF_SYNPROXY_OPT_MSS + } + if e.Wscale != 0 || e.WscaleValueSet { + flags |= NF_SYNPROXY_OPT_WSCALE + } + if e.SackPerm { + flags |= NF_SYNPROXY_OPT_SACK_PERM + } + if e.Timestamp { + flags |= NF_SYNPROXY_OPT_TIMESTAMP + } + if e.Ecn { + flags |= NF_SYNPROXY_OPT_ECN + } + attrs := []netlink.Attribute{ + {Type: NFTA_SYNPROXY_MSS, Data: binaryutil.BigEndian.PutUint16(e.Mss)}, + {Type: NFTA_SYNPROXY_WSCALE, Data: []byte{e.Wscale}}, + {Type: NFTA_SYNPROXY_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)}, + } + return netlink.MarshalAttributes(attrs) +} + +func (e *SynProxy) 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_SYNPROXY_MSS: + e.Mss = ad.Uint16() + case NFTA_SYNPROXY_WSCALE: + e.Wscale = ad.Uint8() + case NFTA_SYNPROXY_FLAGS: + flags := ad.Uint32() + checkFlag := func(flag uint32) bool { + return (flags & flag) == flag + } + e.MssValueSet = checkFlag(NF_SYNPROXY_OPT_MSS) + e.WscaleValueSet = checkFlag(NF_SYNPROXY_OPT_WSCALE) + e.SackPerm = checkFlag(NF_SYNPROXY_OPT_SACK_PERM) + e.Timestamp = checkFlag(NF_SYNPROXY_OPT_TIMESTAMP) + e.Ecn = checkFlag(NF_SYNPROXY_OPT_ECN) + } + } + return ad.Err() +} diff --git a/vendor/github.com/google/nftables/expr/target.go b/vendor/github.com/google/nftables/expr/target.go new file mode 100644 index 000000000..d1c800bbe --- /dev/null +++ b/vendor/github.com/google/nftables/expr/target.go @@ -0,0 +1,82 @@ +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) { + data, err := e.marshalData(fam) + 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) marshalData(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}, + } + + return netlink.MarshalAttributes(attrs) +} + +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 +} diff --git a/vendor/github.com/google/nftables/expr/tproxy.go b/vendor/github.com/google/nftables/expr/tproxy.go new file mode 100644 index 000000000..142740c4d --- /dev/null +++ b/vendor/github.com/google/nftables/expr/tproxy.go @@ -0,0 +1,86 @@ +// 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) { + data, err := e.marshalData(fam) + 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) marshalData(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), + }) + } + + return netlink.MarshalAttributes(attrs) +} + +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() +} diff --git a/vendor/github.com/google/nftables/expr/verdict.go b/vendor/github.com/google/nftables/expr/verdict.go new file mode 100644 index 000000000..239b40878 --- /dev/null +++ b/vendor/github.com/google/nftables/expr/verdict.go @@ -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 ( + "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 + // } + // } + // } + data, err := e.marshalData(fam) + 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) marshalData(fam byte) ([]byte, error) { + 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 + } + + return 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}, + }) +} + +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() +} diff --git a/vendor/github.com/google/nftables/flowtable.go b/vendor/github.com/google/nftables/flowtable.go new file mode 100644 index 000000000..93dbcb55f --- /dev/null +++ b/vendor/github.com/google/nftables/flowtable.go @@ -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("receiveAckAware: %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 +} diff --git a/vendor/github.com/google/nftables/gen.go b/vendor/github.com/google/nftables/gen.go new file mode 100644 index 000000000..4ebcfbeb3 --- /dev/null +++ b/vendor/github.com/google/nftables/gen.go @@ -0,0 +1,45 @@ +package nftables + +import ( + "encoding/binary" + "fmt" + "github.com/mdlayher/netlink" + "golang.org/x/sys/unix" +) + +type GenMsg struct { + ID uint32 + ProcPID uint32 + ProcComm string // [16]byte - max 16bytes - kernel TASK_COMM_LEN +} + +var genHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWGEN) + +func genFromMsg(msg netlink.Message) (*GenMsg, error) { + if got, want := msg.Header.Type, genHeaderType; 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 + + msgOut := &GenMsg{} + for ad.Next() { + switch ad.Type() { + case unix.NFTA_GEN_ID: + msgOut.ID = ad.Uint32() + case unix.NFTA_GEN_PROC_PID: + msgOut.ProcPID = ad.Uint32() + case unix.NFTA_GEN_PROC_NAME: + msgOut.ProcComm = ad.String() + default: + return nil, fmt.Errorf("Unknown attribute: %d %v\n", ad.Type(), ad.Bytes()) + } + } + if err := ad.Err(); err != nil { + return nil, err + } + return msgOut, nil +} diff --git a/vendor/github.com/google/nftables/internal/parseexprfunc/parseexprfunc.go b/vendor/github.com/google/nftables/internal/parseexprfunc/parseexprfunc.go new file mode 100644 index 000000000..586146178 --- /dev/null +++ b/vendor/github.com/google/nftables/internal/parseexprfunc/parseexprfunc.go @@ -0,0 +1,11 @@ +package parseexprfunc + +import ( + "github.com/mdlayher/netlink" +) + +var ( + ParseExprBytesFromNameFunc func(fam byte, ad *netlink.AttributeDecoder, exprName string) ([]interface{}, error) + ParseExprBytesFunc func(fam byte, ad *netlink.AttributeDecoder) ([]interface{}, error) + ParseExprMsgFunc func(fam byte, b []byte) ([]interface{}, error) +) diff --git a/vendor/github.com/google/nftables/monitor.go b/vendor/github.com/google/nftables/monitor.go new file mode 100644 index 000000000..7a25e3b43 --- /dev/null +++ b/vendor/github.com/google/nftables/monitor.go @@ -0,0 +1,377 @@ +// 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<>8 != netlink.HeaderType(unix.NFNL_SUBSYS_NFTABLES) { + continue + } + msgType := msg.Header.Type & 0x00ff + if monitor.monitorFlags&1< 0 { + attrs = append(attrs, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_OBJ_DATA, Data: data}) + } + + 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), cc.marshalAttr(attrs)...), + }) + return o +} + +// DeleteObject deletes the specified Obj +func (cc *Conn) DeleteObject(o Obj) { + cc.mu.Lock() + defer cc.mu.Unlock() + attrs := []netlink.Attribute{ + {Type: unix.NFTA_OBJ_TABLE, Data: []byte(o.table().Name + "\x00")}, + {Type: unix.NFTA_OBJ_NAME, Data: []byte(o.name() + "\x00")}, + {Type: unix.NFTA_OBJ_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(o.objType()))}, + } + data := cc.marshalAttr(attrs) + 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 +// This function returns the same concrete type as passed, +// e.g. QuotaObj, CounterObj or NamedObj. Prefer using the more +// generic NamedObj over the legacy QuotaObj and CounterObj types. +func (cc *Conn) GetObj(o Obj) ([]Obj, error) { + return cc.getObjWithLegacyType(nil, o.table(), unix.NFT_MSG_GETOBJ, cc.useLegacyObjType(o)) +} + +// GetObjReset is a legacy method that reset all Obj that belongs +// the same table as the given one +// This function returns the same concrete type as passed, +// e.g. QuotaObj, CounterObj or NamedObj. Prefer using the more +// generic NamedObj over the legacy QuotaObj and CounterObj types. +func (cc *Conn) GetObjReset(o Obj) ([]Obj, error) { + return cc.getObjWithLegacyType(nil, o.table(), unix.NFT_MSG_GETOBJ_RESET, cc.useLegacyObjType(o)) +} + +// GetObject gets the specified Object +// This function returns the same concrete type as passed, +// e.g. QuotaObj, CounterObj or NamedObj. Prefer using the more +// generic NamedObj over the legacy QuotaObj and CounterObj types. +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 +// This function will always return legacy QuotaObj/CounterObj +// types for backwards compatibility +func (cc *Conn) GetObjects(t *Table) ([]Obj, error) { + return cc.getObj(nil, t, unix.NFT_MSG_GETOBJ) +} + +// GetNamedObjects get all the Obj that belongs to the given table +// This function always return NamedObj types +func (cc *Conn) GetNamedObjects(t *Table) ([]Obj, error) { + return cc.getObjWithLegacyType(nil, t, unix.NFT_MSG_GETOBJ, false) +} + +// ResetObject reset the given Obj +// This function returns the same concrete type as passed, +// e.g. QuotaObj, CounterObj or NamedObj. Prefer using the more +// generic NamedObj over the legacy QuotaObj and CounterObj types. +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 +// This function will always return legacy QuotaObj/CounterObj +// types for backwards compatibility +func (cc *Conn) ResetObjects(t *Table) ([]Obj, error) { + return cc.getObj(nil, t, unix.NFT_MSG_GETOBJ_RESET) +} + +// ResetNamedObjects reset all the Obj that belongs to the given table +// This function always return NamedObj types +func (cc *Conn) ResetNamedObjects(t *Table) ([]Obj, error) { + return cc.getObjWithLegacyType(nil, t, unix.NFT_MSG_GETOBJ_RESET, false) +} + +func objFromMsg(msg netlink.Message, returnLegacyType bool) (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: + if returnLegacyType { + return objDataFromMsgLegacy(ad, table, name, objectType) + } + + o := NamedObj{ + Table: table, + Name: name, + Type: ObjType(objectType), + } + + objs, err := parseexprfunc.ParseExprBytesFromNameFunc(byte(o.family()), ad, objByObjTypeMagic[o.Type]) + if err != nil { + return nil, err + } + if len(objs) == 0 { + return nil, fmt.Errorf("objFromMsg: objs is empty for obj %v", o) + } + exprs := make([]expr.Any, len(objs)) + for i := range exprs { + exprs[i] = objs[i].(expr.Any) + } + + o.Obj = exprs[0] + return &o, ad.Err() + } + } + if err := ad.Err(); err != nil { + return nil, err + } + return nil, fmt.Errorf("malformed stateful object") +} + +func objDataFromMsgLegacy(ad *netlink.AttributeDecoder, table *Table, name string, objectType uint32) (Obj, error) { + 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 unix.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) { + return cc.getObjWithLegacyType(o, t, msgType, cc.useLegacyObjType(o)) +} + +func (cc *Conn) getObjWithLegacyType(o Obj, t *Table, msgType uint16, returnLegacyObjType bool) ([]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 { + attrs := []netlink.Attribute{ + {Type: unix.NFTA_OBJ_TABLE, Data: []byte(o.table().Name + "\x00")}, + {Type: unix.NFTA_OBJ_NAME, Data: []byte(o.name() + "\x00")}, + {Type: unix.NFTA_OBJ_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(o.objType()))}, + } + data = cc.marshalAttr(attrs) + } 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("receiveAckAware: %v", err) + } + var objs []Obj + for _, msg := range reply { + o, err := objFromMsg(msg, returnLegacyObjType) + if err != nil { + return nil, err + } + objs = append(objs, o) + } + + return objs, nil +} + +func (cc *Conn) useLegacyObjType(o Obj) bool { + useLegacyType := true + if o != nil { + switch o.(type) { + case *NamedObj: + useLegacyType = false + } + } + return useLegacyType +} diff --git a/vendor/github.com/google/nftables/quota.go b/vendor/github.com/google/nftables/quota.go new file mode 100644 index 000000000..a8be63465 --- /dev/null +++ b/vendor/github.com/google/nftables/quota.go @@ -0,0 +1,67 @@ +// 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/expr" + "github.com/mdlayher/netlink" + "golang.org/x/sys/unix" +) + +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) != 0 + } + } + return nil +} + +func (q *QuotaObj) table() *Table { + return q.Table +} + +func (q *QuotaObj) family() TableFamily { + return q.Table.Family +} + +func (q *QuotaObj) data() expr.Any { + return &expr.Quota{ + Bytes: q.Bytes, + Consumed: q.Consumed, + Over: q.Over, + } +} + +func (q *QuotaObj) name() string { + return q.Name +} + +func (q *QuotaObj) objType() ObjType { + return ObjTypeQuota +} diff --git a/vendor/github.com/google/nftables/rule.go b/vendor/github.com/google/nftables/rule.go new file mode 100644 index 000000000..07068344e --- /dev/null +++ b/vendor/github.com/google/nftables/rule.go @@ -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("receiveAckAware: %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< 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 + AutoMerge 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 + Comment string + // Indicates that the set has "size" specifier + Size uint32 +} + +// 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 + Comment string +} + +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_USERDATA: + userData := ad.Bytes() + // Try to extract comment from userdata if present + if comment, ok := userdata.GetString(userData, userdata.NFTNL_UDATA_SET_ELEM_COMMENT); ok { + s.Comment = comment + } + case unix.NFTA_SET_ELEM_EXPR: + elems, err := parseexprfunc.ParseExprBytesFunc(fam, ad) + 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 + } + + // Add comment to userdata if present + if len(v.Comment) > 0 { + userData := userdata.AppendString(nil, userdata.NFTNL_UDATA_SET_ELEM_COMMENT, v.Comment) + item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_USERDATA, Data: userData}) + } + + 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}) + } + + var descBytes []byte + + if s.Size > 0 { + // Marshal set size description + descSizeBytes, err := netlink.MarshalAttributes([]netlink.Attribute{ + {Type: unix.NFTA_SET_DESC_SIZE, Data: binaryutil.BigEndian.PutUint32(s.Size)}, + }) + if err != nil { + return fmt.Errorf("fail to marshal set size description: %w", err) + } + + descBytes = append(descBytes, descSizeBytes...) + } + + 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) + } + + descBytes = append(descBytes, concatBytes...) + } + + if len(descBytes) > 0 { + // Marshal set description + tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_SET_DESC, Data: descBytes}) + } + + // https://git.netfilter.org/libnftnl/tree/include/udata.h#n17 + var userData []byte + + if s.Anonymous || s.Constant || s.Interval || s.KeyByteOrder == binaryutil.BigEndian { + // Semantically useless - kept for binary compatability with nft + userData = userdata.AppendUint32(userData, userdata.NFTNL_UDATA_SET_KEYBYTEORDER, 2) + } else if s.KeyByteOrder == binaryutil.NativeEndian { + // Per https://git.netfilter.org/nftables/tree/src/mnl.c?id=187c6d01d35722618c2711bbc49262c286472c8f#n1165 + userData = userdata.AppendUint32(userData, userdata.NFTNL_UDATA_SET_KEYBYTEORDER, 1) + } + + if s.Interval && s.AutoMerge { + // https://git.netfilter.org/nftables/tree/src/mnl.c?id=187c6d01d35722618c2711bbc49262c286472c8f#n1174 + userData = userdata.AppendUint32(userData, userdata.NFTNL_UDATA_SET_MERGE_ELEMENTS, 1) + } + + if len(s.Comment) != 0 { + userData = userdata.AppendString(userData, userdata.NFTNL_UDATA_SET_COMMENT, s.Comment) + } + + if len(userData) > 0 { + tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_USERDATA, Data: userData}) + } + + 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.Dynamic = (flags & unix.NFT_SET_EVAL) != 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()) + case unix.NFTA_SET_USERDATA: + data := ad.Bytes() + value, ok := userdata.GetUint32(data, userdata.NFTNL_UDATA_SET_MERGE_ELEMENTS) + set.AutoMerge = ok && value == 1 + case unix.NFTA_SET_DESC: + nestedAD, err := netlink.NewAttributeDecoder(ad.Bytes()) + if err != nil { + return nil, fmt.Errorf("nested NewAttributeDecoder() failed: %w", err) + } + for nestedAD.Next() { + switch nestedAD.Type() { + case unix.NFTA_SET_DESC_SIZE: + set.Size = binary.BigEndian.Uint32(nestedAD.Bytes()) + } + } + if nestedAD.Err() != nil { + return nil, fmt.Errorf("decoding set description: %w", nestedAD.Err()) + } + } + } + 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("receiveAckAware: %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("receiveAckAware: %w", err) + } + + if len(reply) != 1 { + return nil, fmt.Errorf("receiveAckAware: 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("receiveAckAware: %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 +} diff --git a/vendor/github.com/google/nftables/table.go b/vendor/github.com/google/nftables/table.go new file mode 100644 index 000000000..c391b7be2 --- /dev/null +++ b/vendor/github.com/google/nftables/table.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/userdata/userdata.go b/vendor/github.com/google/nftables/userdata/userdata.go new file mode 100644 index 000000000..984d84c8d --- /dev/null +++ b/vendor/github.com/google/nftables/userdata/userdata.go @@ -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 userdata implements a TLV parser/serializer for libnftables-compatible comments +package userdata + +import ( + "bytes" + "encoding/binary" +) + +type Type byte + +// TLV type values are defined in: +// https://git.netfilter.org/iptables/tree/iptables/nft.c?id=73611d5582e72367a698faf1b5301c836e981465#n1659 +const ( + TypeComment Type = iota + TypeEbtablesPolicy + + TypesCount +) + +// TLV type values are defined in: +// https://git.netfilter.org/libnftnl/tree/include/libnftnl/udata.h#n39 +const ( + NFTNL_UDATA_SET_KEYBYTEORDER Type = iota + NFTNL_UDATA_SET_DATABYTEORDER + NFTNL_UDATA_SET_MERGE_ELEMENTS + NFTNL_UDATA_SET_KEY_TYPEOF + NFTNL_UDATA_SET_DATA_TYPEOF + NFTNL_UDATA_SET_EXPR + NFTNL_UDATA_SET_DATA_INTERVAL + NFTNL_UDATA_SET_COMMENT + + NFTNL_UDATA_SET_MAX +) + +// Set element userdata types +const ( + NFTNL_UDATA_SET_ELEM_COMMENT Type = iota + NFTNL_UDATA_SET_ELEM_FLAGS +) + +func Append(udata []byte, typ Type, data []byte) []byte { + udata = append(udata, byte(typ), byte(len(data))) + udata = append(udata, data...) + + return udata +} + +func Get(udata []byte, styp Type) []byte { + for { + if len(udata) < 2 { + break + } + + typ := Type(udata[0]) + length := int(udata[1]) + data := udata[2 : 2+length] + + if styp == typ { + return data + } + + if len(udata) < 2+length { + break + } else { + udata = udata[2+length:] + } + } + + return nil +} + +func AppendUint32(udata []byte, typ Type, num uint32) []byte { + data := binary.LittleEndian.AppendUint32(nil, num) + + return Append(udata, typ, data) +} + +func GetUint32(udata []byte, typ Type) (uint32, bool) { + data := Get(udata, typ) + if data == nil { + return 0, false + } + + return binary.LittleEndian.Uint32(data), true +} + +func AppendString(udata []byte, typ Type, str string) []byte { + data := append([]byte(str), 0) + return Append(udata, typ, data) +} + +func GetString(udata []byte, typ Type) (string, bool) { + data := Get(udata, typ) + if data == nil { + return "", false + } + + data, _ = bytes.CutSuffix(data, []byte{0}) + + return string(data), true +} diff --git a/vendor/github.com/google/nftables/util.go b/vendor/github.com/google/nftables/util.go new file mode 100644 index 000000000..b040ae4c2 --- /dev/null +++ b/vendor/github.com/google/nftables/util.go @@ -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) < 4 { + 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 +} diff --git a/vendor/github.com/google/nftables/xt/comment.go b/vendor/github.com/google/nftables/xt/comment.go new file mode 100644 index 000000000..830773a95 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/comment.go @@ -0,0 +1,34 @@ +package xt + +import ( + "bytes" + "fmt" +) + +// CommentSize is the fixed size of a comment info xt blob, see: +// https://elixir.bootlin.com/linux/v6.8.7/source/include/uapi/linux/netfilter/xt_comment.h#L5 +const CommentSize = 256 + +// Comment gets marshalled and unmarshalled as a fixed-sized char array, filled +// with zeros as necessary, see: +// https://elixir.bootlin.com/linux/v6.8.7/source/include/uapi/linux/netfilter/xt_comment.h#L7 +type Comment string + +func (c *Comment) marshal(fam TableFamily, rev uint32) ([]byte, error) { + if len(*c) >= CommentSize { + return nil, fmt.Errorf("comment must be less than %d bytes, got %d bytes", + CommentSize, len(*c)) + } + data := make([]byte, CommentSize) + copy(data, []byte(*c)) + return data, nil +} + +func (c *Comment) unmarshal(fam TableFamily, rev uint32, data []byte) error { + if len(data) != CommentSize { + return fmt.Errorf("malformed comment: got %d bytes, expected exactly %d bytes", + len(data), CommentSize) + } + *c = Comment(bytes.TrimRight(data, "\x00")) + return nil +} diff --git a/vendor/github.com/google/nftables/xt/info.go b/vendor/github.com/google/nftables/xt/info.go new file mode 100644 index 000000000..4706ba5fc --- /dev/null +++ b/vendor/github.com/google/nftables/xt/info.go @@ -0,0 +1,97 @@ +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 "comment": + var c Comment + i = &c + 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 +} diff --git a/vendor/github.com/google/nftables/xt/match_addrtype.go b/vendor/github.com/google/nftables/xt/match_addrtype.go new file mode 100644 index 000000000..3e21057a1 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/match_addrtype.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/xt/match_conntrack.go b/vendor/github.com/google/nftables/xt/match_conntrack.go new file mode 100644 index 000000000..69c51bd80 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/match_conntrack.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/xt/match_tcp.go b/vendor/github.com/google/nftables/xt/match_tcp.go new file mode 100644 index 000000000..d991f1276 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/match_tcp.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/xt/match_udp.go b/vendor/github.com/google/nftables/xt/match_udp.go new file mode 100644 index 000000000..68ce12a06 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/match_udp.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/xt/target_dnat.go b/vendor/github.com/google/nftables/xt/target_dnat.go new file mode 100644 index 000000000..b54e8fbef --- /dev/null +++ b/vendor/github.com/google/nftables/xt/target_dnat.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/xt/target_masquerade_ip.go b/vendor/github.com/google/nftables/xt/target_masquerade_ip.go new file mode 100644 index 000000000..411d3beaa --- /dev/null +++ b/vendor/github.com/google/nftables/xt/target_masquerade_ip.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/xt/unknown.go b/vendor/github.com/google/nftables/xt/unknown.go new file mode 100644 index 000000000..c648307c5 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/unknown.go @@ -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 +} diff --git a/vendor/github.com/google/nftables/xt/util.go b/vendor/github.com/google/nftables/xt/util.go new file mode 100644 index 000000000..673ac54f7 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/util.go @@ -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)) +} diff --git a/vendor/github.com/google/nftables/xt/xt.go b/vendor/github.com/google/nftables/xt/xt.go new file mode 100644 index 000000000..d8977c1d0 --- /dev/null +++ b/vendor/github.com/google/nftables/xt/xt.go @@ -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 diff --git a/vendor/github.com/mattn/go-shellwords/.gitignore b/vendor/github.com/mattn/go-shellwords/.gitignore new file mode 100644 index 000000000..970ea629a --- /dev/null +++ b/vendor/github.com/mattn/go-shellwords/.gitignore @@ -0,0 +1 @@ +.codex diff --git a/vendor/github.com/mattn/go-shellwords/README.md b/vendor/github.com/mattn/go-shellwords/README.md index bdd531918..1dbca1402 100644 --- a/vendor/github.com/mattn/go-shellwords/README.md +++ b/vendor/github.com/mattn/go-shellwords/README.md @@ -42,6 +42,18 @@ args, err := p.Parse("./foo `echo $SHELL`") // args should be ["./foo", "/bin/bash"] ``` +```go +p := shellwords.NewParser() +p.ParseComment = true +args, err := p.Parse("./foo # comment") +// args should be ["./foo"] +``` + +```go +p := shellwords.NewParser() +p.SetExcludeSeparators('\t', ';') +``` + # Thanks This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine). diff --git a/vendor/github.com/mattn/go-shellwords/shellwords.go b/vendor/github.com/mattn/go-shellwords/shellwords.go index 3747e3549..3e211b02d 100644 --- a/vendor/github.com/mattn/go-shellwords/shellwords.go +++ b/vendor/github.com/mattn/go-shellwords/shellwords.go @@ -11,8 +11,11 @@ import ( var ( ParseEnv bool = false ParseBacktick bool = false + ParseComment bool = false ) +var errInvalidCmdLine = errors.New("invalid command line string") + func isSpace(r rune) bool { switch r { case ' ', '\t', '\r', '\n': @@ -63,7 +66,7 @@ func replaceEnv(getenv func(string) string, s string) string { return s } if i > p { - buf.WriteString(getenv(s[p:i])) + buf.WriteString(getenv(string(rs[p:i]))) } } else { p := i @@ -81,10 +84,11 @@ func replaceEnv(getenv func(string) string, s string) string { } } if i > p { - buf.WriteString(getenv(s[p:i])) + buf.WriteString(getenv(string(rs[p:i]))) i-- } else { - buf.WriteString(s[p:]) + buf.WriteRune('$') + i-- } } } else { @@ -97,8 +101,10 @@ func replaceEnv(getenv func(string) string, s string) string { type Parser struct { ParseEnv bool ParseBacktick bool + ParseComment bool Position int Dir string + excludedSep []rune // If ParseEnv is true, use this for getenv. // If nil, use os.Getenv. @@ -109,8 +115,10 @@ func NewParser() *Parser { return &Parser{ ParseEnv: ParseEnv, ParseBacktick: ParseBacktick, + ParseComment: ParseComment, Position: 0, Dir: "", + excludedSep: []rune{}, } } @@ -122,62 +130,125 @@ const ( argQuoted ) +// isExcluded checks if separator should be ignored +func (p *Parser) isExcluded(r rune) bool { + for _, v := range p.excludedSep { + if v == r { + return true + } + } + return false +} + +// SetExcludeSeparators indicates the parser to ignore provided separators when parsing +// example: parser.SetExcludeSeparators(';','\t') +func (p *Parser) SetExcludeSeparators(r ...rune) { + p.excludedSep = r +} + +// ExcludedSeparators returns excluded separators +func (p *Parser) ExcludedSeparators() []rune { + return p.excludedSep +} + func (p *Parser) Parse(line string) ([]string, error) { - args := []string{} - buf := "" - var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool - backtick := "" + args := make([]string, 0, 1+len(line)/8) + buf := make([]byte, 0, len(line)) + var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote, comment bool + var backtick []byte pos := -1 got := argNo + flush := func() error { + if got == argQuoted || (got != argNo && len(buf) > 0) { + token := string(buf) + if p.ParseEnv { + if got == argSingle { + parser := &Parser{ParseEnv: false, ParseBacktick: false, Position: 0, Dir: p.Dir} + strs, err := parser.Parse(replaceEnv(p.Getenv, token)) + if err != nil { + return err + } + args = append(args, strs...) + } else { + args = append(args, replaceEnv(p.Getenv, token)) + } + } else { + args = append(args, token) + } + } + buf = buf[:0] + got = argNo + return nil + } + i := -1 loop: for _, r := range line { i++ + + if comment { + if r == '\n' { + comment = false + } + continue + } + if escaped { + escaped = false + if backQuote || dollarQuote { + buf = append(buf, '\\') + buf = append(buf, string(r)...) + backtick = append(backtick, '\\') + backtick = append(backtick, string(r)...) + got = argSingle + continue + } + if p.ParseEnv && r == '$' { + // Keep the backslash so replaceEnv treats the '$' as + // literal instead of expanding it. + buf = append(buf, '\\', '$') + got = argSingle + continue + } if r == 't' { r = '\t' } if r == 'n' { r = '\n' } - buf += string(r) - escaped = false + buf = append(buf, string(r)...) got = argSingle continue } if r == '\\' { if singleQuoted { - buf += string(r) + buf = append(buf, '\\') } else { escaped = true } continue } + if p.isExcluded(r) { + got = argSingle + buf = append(buf, string(r)...) + if backQuote || dollarQuote { + backtick = append(backtick, string(r)...) + } + continue + } + if isSpace(r) { if singleQuoted || doubleQuoted || backQuote || dollarQuote { - buf += string(r) - backtick += string(r) - } else if got != argNo { - if p.ParseEnv { - if got == argSingle { - parser := &Parser{ParseEnv: false, ParseBacktick: false, Position: 0, Dir: p.Dir} - strs, err := parser.Parse(replaceEnv(p.Getenv, buf)) - if err != nil { - return nil, err - } - args = append(args, strs...) - } else { - args = append(args, replaceEnv(p.Getenv, buf)) - } - } else { - args = append(args, buf) + buf = append(buf, byte(r)) + if backQuote || dollarQuote { + backtick = append(backtick, byte(r)) } - buf = "" - got = argNo + } else if err := flush(); err != nil { + return nil, err } continue } @@ -187,17 +258,17 @@ loop: if !singleQuoted && !doubleQuoted && !dollarQuote { if p.ParseBacktick { if backQuote { - out, err := shellRun(backtick, p.Dir) + out, err := shellRun(string(backtick), p.Dir) if err != nil { return nil, err } - buf = buf[:len(buf)-len(backtick)] + out + buf = append(buf[:len(buf)-len(backtick)], out...) } - backtick = "" + backtick = backtick[:0] backQuote = !backQuote continue } - backtick = "" + backtick = backtick[:0] backQuote = !backQuote } @@ -209,10 +280,10 @@ loop: // Preserve prior behavior by rejecting unmatched ')' // when command substitution parsing is enabled. if !dollarQuote { - return nil, errors.New("invalid command line string") + return nil, errInvalidCmdLine } - out, err := shellRun(backtick, p.Dir) + out, err := shellRun(string(backtick), p.Dir) if err != nil { return nil, err } @@ -220,11 +291,11 @@ loop: // Defensive guard: valid $(...) implies the buffer must contain // the "$(" prefix plus the collected command body. if len(buf) < len(backtick)+2 { - return nil, errors.New("invalid command line string") + return nil, errInvalidCmdLine } - buf = buf[:len(buf)-len(backtick)-2] + out - backtick = "" + buf = append(buf[:len(buf)-len(backtick)-2], out...) + backtick = backtick[:0] dollarQuote = false continue } @@ -233,11 +304,11 @@ loop: // A bare ')' is a syntax error, consistent with '(' handling. // Only close an already-open $(...) region. if !dollarQuote { - return nil, errors.New("invalid command line string") + return nil, errInvalidCmdLine } - buf += string(r) - backtick = "" + buf = append(buf, ')') + backtick = backtick[:0] dollarQuote = false got = argSingle continue @@ -245,17 +316,17 @@ loop: case '(': if !singleQuoted && !doubleQuoted && !backQuote { - if !dollarQuote && strings.HasSuffix(buf, "$") { + if n := len(buf); !dollarQuote && n > 0 && buf[n-1] == '$' && !(n > 1 && buf[n-2] == '\\') { dollarQuote = true - buf += "(" + buf = append(buf, '(') continue } else { - return nil, errors.New("invalid command line string") + return nil, errInvalidCmdLine } } case '"': - if !singleQuoted && !dollarQuote { + if !singleQuoted && !dollarQuote && !backQuote { if doubleQuoted { got = argQuoted } @@ -264,7 +335,7 @@ loop: } case '\'': - if !doubleQuoted && !dollarQuote { + if !doubleQuoted && !dollarQuote && !backQuote { if singleQuoted { got = argQuoted } @@ -275,42 +346,41 @@ loop: case ';', '&', '|', '<', '>': if !(escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote) { if r == '>' && len(buf) > 0 { - if c := buf[0]; '0' <= c && c <= '9' { - i -= 1 + isDigits := true + for _, c := range buf { + if c < '0' || c > '9' { + isDigits = false + break + } + } + if isDigits { + i -= len(buf) got = argNo } } pos = i break loop } + case '#': + if p.ParseComment && len(buf) == 0 && !(escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote) { + comment = true + continue loop + } } got = argSingle - buf += string(r) + buf = append(buf, string(r)...) if backQuote || dollarQuote { - backtick += string(r) + backtick = append(backtick, string(r)...) } } - if got != argNo { - if p.ParseEnv { - if got == argSingle { - parser := &Parser{ParseEnv: false, ParseBacktick: false, Position: 0, Dir: p.Dir} - strs, err := parser.Parse(replaceEnv(p.Getenv, buf)) - if err != nil { - return nil, err - } - args = append(args, strs...) - } else { - args = append(args, replaceEnv(p.Getenv, buf)) - } - } else { - args = append(args, buf) - } + if err := flush(); err != nil { + return nil, err } if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote { - return nil, errors.New("invalid command line string") + return nil, errInvalidCmdLine } p.Position = pos @@ -340,7 +410,18 @@ func (p *Parser) ParseWithEnvs(line string) (envs []string, args []string, err e } func isEnv(arg string) bool { - return len(strings.Split(arg, "=")) == 2 + i := strings.IndexByte(arg, '=') + if i <= 0 { + return false + } + for j := 0; j < i; j++ { + c := arg[j] + if c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (j > 0 && '0' <= c && c <= '9') { + continue + } + return false + } + return true } func Parse(line string) ([]string, error) { diff --git a/vendor/github.com/mdlayher/netlink/.gitignore b/vendor/github.com/mdlayher/netlink/.gitignore new file mode 100644 index 000000000..efc8a0a9c --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/.gitignore @@ -0,0 +1,4 @@ +internal/integration/integration.test +netlink.test +netlink-fuzz.zip +testdata/ diff --git a/vendor/github.com/mdlayher/netlink/.golangci.yml b/vendor/github.com/mdlayher/netlink/.golangci.yml new file mode 100644 index 000000000..7237e2714 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/.golangci.yml @@ -0,0 +1,21 @@ +version: "2" +linters: + enable: + # - errorlint + - misspell + - modernize + - revive + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - errcheck + path: _test.go +formatters: + exclusions: + generated: lax diff --git a/vendor/github.com/mdlayher/netlink/CHANGELOG.md b/vendor/github.com/mdlayher/netlink/CHANGELOG.md new file mode 100644 index 000000000..888831869 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/CHANGELOG.md @@ -0,0 +1,266 @@ +# CHANGELOG + +## v1.11.2 + +- [Bug Fix]: [#291](https://github.com/mdlayher/netlink/pull/291) fixed a bug + where `netlink.Conn.Receive` would block concurrent calls to + `netlink.Conn.Send` when blocking in `recvmsg`. +- [Improvement]: [#289](https://github.com/mdlayher/netlink/pull/289) updated + `golang.org/x/net` and `golang.org/x/sys` dependencies. + +## v1.11.1 + +- [Bug Fix]: [#283](https://github.com/mdlayher/netlink/pull/283) fixed a bug + where `netlink.Conn.Receive` would reject valid netlink messages whose header + length is not aligned. This is common with nfqueue, nflog and conntrack events. +- [Bug Fix]: [#284](https://github.com/mdlayher/netlink/pull/284) fixed a bug + where `netlink.Message.Data` could be silently overwritten by subsequent + `netlink.Conn.Receive` calls when `netlink.Config.MessageBufferSize` is enabled, + due to pooled buffer reuse. +- [Improvement]: [#282](https://github.com/mdlayher/netlink/pull/282) improved + error handling by usage of `errors` package. + +## v1.11.0 + +**This is the first release of package netlink that only supports Go 1.25+.** + +- [Bug Fix]: [#280](https://github.com/mdlayher/netlink/pull/280) fixed a + critical bug where `netlink.Conn.Receive` and `netlink.Conn.ReceiveIter` would + panic if the received message was unaligned. +- [Improvement]: [#277](https://github.com/mdlayher/netlink/pull/277) added + big-endian test fixtures for nlenc and deprecated endian helpers in favor of + binary.NativeEndian. +- [Improvement]: [#276](https://github.com/mdlayher/netlink/pull/276) fixed + skipping of tests on big-endian hosts. +- [Improvement]: [#273](https://github.com/mdlayher/netlink/pull/273) added + golangci-lint to the CI pipeline and fixed all existing lint issues. +- [Improvement]: [#272](https://github.com/mdlayher/netlink/pull/272) updated + dependencies and Go version to 1.25. + +## v1.10.0 + +**Users of this version should upgrade to v1.11.0 as this version contains a +critical bug.** + +- [New API]: [#270](https://github.com/mdlayher/netlink/pull/270) added + `MessageBufferSize` option to `netlink.Config` for configuring the size of the + copy buffer used for receiving messages. This can improve performance in + high-throughput applications by reducing the number of syscalls needed to + receive messages. +- [Improvement]: [#219](https://github.com/mdlayher/netlink/pull/219) added + debug logging inspired by libmnl. This is now the new default when setting + `NLDEBUG` in the environment. +- [New API]: [#258](https://github.com/mdlayher/netlink/pull/258) added + `netlink.Conn.ReceiveIter` for iterating over responses without collecting + them into a slice. It also optimized `netlink.Conn.Receive` to use this new + API internally so that less memory is used. +- [Improvement]: [#269](https://github.com/mdlayher/netlink/pull/269) made it so + `nltest.Conn.Receive` can be used to test that multi-part messages are drained + properly. +- [Improvement]: [#266](https://github.com/mdlayher/netlink/pull/266) optimized + the initial parsing of netlink messages in `netlink.Socket.Receive` by adding + an iterator for parsing messages directly from the receive buffer. +- [Improvement]: [#267](https://github.com/mdlayher/netlink/pull/267) added + integration test benchmarks for multi-part dumps. +- [Improvement]: [#215](https://github.com/mdlayher/netlink/pull/215) optimized + the internal peek/allocate logic in `netlink.Socket.Receive`. Messages are + no longer being copied during peeking and the buffer is allocated to the exact + size of the upcoming message. +- [Bug Fix]: [#265](https://github.com/mdlayher/netlink/pull/265) fixed a bug + where concurrent calls to `netlink.Conn.Receive` could race with each other + when handling multi-part messages. Calls to `Receive` are now serialized. +- [Improvement]: [#264](https://github.com/mdlayher/netlink/pull/264) added + handling for truncated messages in `netlink.Socket.Receive`. + +## v1.9.0 + +**This is the first release of package netlink that only supports Go 1.24+.** + +- [Improvement]: updated dependencies and Go version to 1.24; tests now run on + Go 1.24-1.26. +- [New API]: [#236](https://github.com/mdlayher/netlink/pull/236) introduced + `Sequence` field to `OpError` for better error correlation. +- [New API]: [#237](https://github.com/mdlayher/netlink/pull/237) added + `netlink.Conn.PID` method to retrieve the connection's PID, also known as port + ID. +- [Improvement]: [#228](https://github.com/mdlayher/netlink/pull/228) fixed + skipping of specific tests on big-endian hosts. + +## v1.8.0 + +- [Improvement]: Updated dependencies, test with Go 1.23 to 1.25. +- [Improvement]: Use Go 1.21's binary.NativeEndian (#220) +- [Improvement]: Expose socket ReadBuffer & WriteBuffer functions (#223) + +## v1.7.2 + +- [Improvement]: updated dependencies, test with Go 1.20. + +## v1.7.1 + +- [Bug Fix]: test only changes to avoid failures on big endian machines. + +## v1.7.0 + +**This is the first release of package netlink that only supports Go 1.18+. +Users on older versions of Go must use v1.6.2.** + +- [Improvement]: drop support for older versions of Go so we can begin using + modern versions of `x/sys` and other dependencies. + +## v1.6.2 + +**This is the last release of package netlink that supports Go 1.17 and below.** + +- [Bug Fix] [commit](https://github.com/mdlayher/netlink/commit/9f7f860d9865069cd1a6b4dee32a3095f0b841fc): + undo update to `golang.org/x/sys` which would force the minimum Go version of + this package to Go 1.17 due to use of `unsafe.Slice`. We encourage users to + use the latest stable version of Go where possible, but continue to maintain + some compatibility with older versions of Go as long as it is reasonable to do + so. + +## v1.6.1 + +- [Deprecation] [commit](https://github.com/mdlayher/netlink/commit/d1b69ea8697d721415c259ef8513ab699c6d3e96): + the `netlink.Socket` interface has been marked as deprecated. The abstraction + is awkward to use properly and disables much of the functionality of the Conn + type when the basic interface is implemented. Do not use. + +## v1.6.0 + +**This is the first release of package netlink that only supports Go 1.13+. +Users on older versions of Go must use v1.5.0.** + +- [New API] [commit](https://github.com/mdlayher/netlink/commit/ad9e2c41caa993e3f4b68831d6cb2cb05818275d): + the `netlink.Config.Strict` field can be used to apply a more strict default + set of options to a `netlink.Conn`. This is recommended for applications + running on modern Linux kernels, but cannot be enabled by default because the + options may require a more recent kernel than the minimum kernel version that + Go supports. See the documentation for details. +- [Improvement]: broke some integration tests into a separate Go module so the + default `go.mod` for package `netlink` has fewer dependencies. + +## v1.5.0 + +**This is the last release of package netlink that supports Go 1.12.** + +- [New API] [commit](https://github.com/mdlayher/netlink/commit/53a1c10065e51077659ceedf921c8f0807abe8c0): + the `netlink.Config.PID` field can be used to specify an explicit port ID when + binding the netlink socket. This is intended for advanced use cases and most + callers should leave this field set to 0. +- [Improvement]: more low-level functionality ported to + `github.com/mdlayher/socket`, reducing package complexity. + +## v1.4.2 + +- [Documentation] [commit](https://github.com/mdlayher/netlink/commit/177e6364fb170d465d681c7c8a6283417a6d3e49): + the `netlink.Config.DisableNSLockThread` now properly uses Go's deprecated + identifier convention. This option has been a noop for a long time and should + not be used. +- [Improvement] [#189](https://github.com/mdlayher/netlink/pull/189): the + package now uses Go 1.17's `//go:build` identifiers. Thanks @tklauser. +- [Bug Fix] + [commit](https://github.com/mdlayher/netlink/commit/fe6002e030928bd1f2a446c0b6c65e8f2df4ed5e): + the `netlink.AttributeEncoder`'s `Bytes`, `String`, and `Do` methods now + properly reject byte slices and strings which are too large to fit in the + value of a netlink attribute. Thanks @ubiquitousbyte for the report. + +## v1.4.1 + +- [Improvement]: significant runtime network poller integration cleanup through + the use of `github.com/mdlayher/socket`. + +## v1.4.0 + +- [New API] [#185](https://github.com/mdlayher/netlink/pull/185): the + `netlink.AttributeDecoder` and `netlink.AttributeEncoder` types now have + methods for dealing with signed integers: `Int8`, `Int16`, `Int32`, and + `Int64`. These are necessary for working with rtnetlink's XDP APIs. Thanks + @fbegyn. + +## v1.3.2 + +- [Improvement] + [commit](https://github.com/mdlayher/netlink/commit/ebc6e2e28bcf1a0671411288423d8116ff924d6d): + `github.com/google/go-cmp` is no longer a (non-test) dependency of this module. + +## v1.3.1 + +- [Improvement]: many internal cleanups and simplifications. The library is now + slimmer and features less internal indirection. There are no user-facing + changes in this release. + +## v1.3.0 + +- [New API] [#176](https://github.com/mdlayher/netlink/pull/176): + `netlink.OpError` now has `Message` and `Offset` fields which are populated + when the kernel returns netlink extended acknowledgement data along with an + error code. The caller can turn on this option by using + `netlink.Conn.SetOption(netlink.ExtendedAcknowledge, true)`. +- [New API] + [commit](https://github.com/mdlayher/netlink/commit/beba85e0372133b6d57221191d2c557727cd1499): + the `netlink.GetStrictCheck` option can be used to tell the kernel to be more + strict when parsing requests. This enables more safety checks and can allow + the kernel to perform more advanced request filtering in subsystems such as + route netlink. + +## v1.2.1 + +- [Bug Fix] + [commit](https://github.com/mdlayher/netlink/commit/d81418f81b0bfa2465f33790a85624c63d6afe3d): + `netlink.SetBPF` will no longer panic if an empty BPF filter is set. +- [Improvement] + [commit](https://github.com/mdlayher/netlink/commit/8014f9a7dbf4fd7b84a1783dd7b470db9113ff36): + the library now uses https://github.com/josharian/native to provide the + system's native endianness at compile time, rather than re-computing it many + times at runtime. + +## v1.2.0 + +**This is the first release of package netlink that only supports Go 1.12+. +Users on older versions of Go must use v1.1.1.** + +- [Improvement] [#173](https://github.com/mdlayher/netlink/pull/173): support + for Go 1.11 and below has been dropped. All users are highly recommended to + use a stable and supported release of Go for their applications. +- [Performance] [#171](https://github.com/mdlayher/netlink/pull/171): + `netlink.Conn` no longer requires a locked OS thread for the vast majority of + operations, which should result in a significant speedup for highly concurrent + callers. Thanks @ti-mo. +- [Bug Fix] [#169](https://github.com/mdlayher/netlink/pull/169): calls to + `netlink.Conn.Close` are now able to unblock concurrent calls to + `netlink.Conn.Receive` and other blocking operations. + +## v1.1.1 + +**This is the last release of package netlink that supports Go 1.11.** + +- [Improvement] [#165](https://github.com/mdlayher/netlink/pull/165): + `netlink.Conn` `SetReadBuffer` and `SetWriteBuffer` methods now attempt the + `SO_*BUFFORCE` socket options to possibly ignore system limits given elevated + caller permissions. Thanks @MarkusBauer. +- [Note] + [commit](https://github.com/mdlayher/netlink/commit/c5f8ab79aa345dcfcf7f14d746659ca1b80a0ecc): + `netlink.Conn.Close` has had a long-standing bug + [#162](https://github.com/mdlayher/netlink/pull/162) related to internal + concurrency handling where a call to `Close` is not sufficient to unblock + pending reads. To effectively fix this issue, it is necessary to drop support + for Go 1.11 and below. This will be fixed in a future release, but a + workaround is noted in the method documentation as of now. + +## v1.1.0 + +- [New API] [#157](https://github.com/mdlayher/netlink/pull/157): the + `netlink.AttributeDecoder.TypeFlags` method enables retrieval of the type bits + stored in a netlink attribute's type field, because the existing `Type` method + masks away these bits. Thanks @ti-mo! +- [Performance] [#157](https://github.com/mdlayher/netlink/pull/157): `netlink.AttributeDecoder` + now decodes netlink attributes on demand, enabling callers who only need a + limited number of attributes to exit early from decoding loops. Thanks @ti-mo! +- [Improvement] [#161](https://github.com/mdlayher/netlink/pull/161): `netlink.Conn` + system calls are now ready for Go 1.14+'s changes to goroutine preemption. + See the PR for details. + +## v1.0.0 + +- Initial stable commit. diff --git a/vendor/github.com/mdlayher/netlink/LICENSE.md b/vendor/github.com/mdlayher/netlink/LICENSE.md new file mode 100644 index 000000000..1eea1a534 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/LICENSE.md @@ -0,0 +1,9 @@ +# MIT License + +Copyright (C) 2016-2026 Matt Layher + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mdlayher/netlink/README.md b/vendor/github.com/mdlayher/netlink/README.md new file mode 100644 index 000000000..c41de0d35 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/README.md @@ -0,0 +1,183 @@ +# netlink [![Test Status](https://github.com/mdlayher/netlink/workflows/Linux%20Test/badge.svg)](https://github.com/mdlayher/netlink/actions) [![Go Reference](https://pkg.go.dev/badge/github.com/mdlayher/netlink.svg)](https://pkg.go.dev/github.com/mdlayher/netlink) [![Go Report Card](https://goreportcard.com/badge/github.com/mdlayher/netlink)](https://goreportcard.com/report/github.com/mdlayher/netlink) + +Package `netlink` provides low-level access to Linux netlink sockets +(`AF_NETLINK`). MIT Licensed. + +For more information about how netlink works, check out my blog series +on [Linux, Netlink, and Go](https://mdlayher.com/blog/linux-netlink-and-go-part-1-netlink/). + +If you have any questions or you'd like some guidance, please join us on +[Gophers Slack](https://invite.slack.golangbridge.org) in the `#networking` +channel! + +## Stability + +See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between +releases. + +This package has a stable v1 API and any future breaking changes will prompt +the release of a new major version. Features and bug fixes will continue to +occur in the v1.x.x series. + +This package only supports the two most recent major versions of Go, mirroring +Go's own release policy. Older versions of Go may lack critical features and bug +fixes which are necessary for this package to function correctly. + +## Design + +A number of netlink packages are already available for Go, but I wasn't able to +find one that aligned with what I wanted in a netlink package: + +- Straightforward, idiomatic API +- Well tested +- Well documented +- Doesn't use package/global variables or state +- Doesn't necessarily need root to work + +My goal for this package is to use it as a building block for the creation +of other netlink family packages. + +## Ecosystem + +Over time, an ecosystem of Go packages has developed around package `netlink`. +Many of these packages provide building blocks for further interactions with +various netlink families, such as `NETLINK_GENERIC` or `NETLINK_ROUTE`. + +To have your package included in this diagram, please send a pull request! + +```mermaid +flowchart LR + netlink["github.com/mdlayher/netlink"] + click netlink "https://github.com/mdlayher/netlink" + + subgraph "NETLINK_CONNECTOR" + direction LR + + garlic["github.com/fearful-symmetry/garlic"] + click garlic "https://github.com/fearful-symmetry/garlic" + end + + subgraph "NETLINK_CRYPTO" + direction LR + + cryptonl["github.com/mdlayher/cryptonl"] + click cryptonl "https://github.com/mdlayher/cryptonl" + end + + subgraph "NETLINK_GENERIC" + direction LR + + genetlink["github.com/mdlayher/genetlink"] + click genetlink "https://github.com/mdlayher/genetlink" + + devlink["github.com/mdlayher/devlink"] + click devlink "https://github.com/mdlayher/devlink" + + ethtool["github.com/mdlayher/ethtool"] + click ethtool "https://github.com/mdlayher/ethtool" + + go-openvswitch["github.com/digitalocean/go-openvswitch"] + click go-openvswitch "https://github.com/digitalocean/go-openvswitch" + + ipvs["github.com/cloudflare/ipvs"] + click ipvs "https://github.com/cloudflare/ipvs" + + l2tp["github.com/axatrax/l2tp"] + click l2tp "https://github.com/axatrax/l2tp" + + nbd["github.com/Merovius/nbd"] + click nbd "https://github.com/Merovius/nbd" + + quota["github.com/mdlayher/quota"] + click quota "https://github.com/mdlayher/quota" + + router7["github.com/rtr7/router7"] + click router7 "https://github.com/rtr7/router7" + + taskstats["github.com/mdlayher/taskstats"] + click taskstats "https://github.com/mdlayher/taskstats" + + u-bmc["github.com/u-root/u-bmc"] + click u-bmc "https://github.com/u-root/u-bmc" + + wgctrl["golang.zx2c4.com/wireguard/wgctrl"] + click wgctrl "https://golang.zx2c4.com/wireguard/wgctrl" + + wifi["github.com/mdlayher/wifi"] + click wifi "https://github.com/mdlayher/wifi" + + devlink & ethtool & go-openvswitch & ipvs --> genetlink + l2tp & nbd & quota & router7 & taskstats --> genetlink + u-bmc & wgctrl & wifi --> genetlink + end + + subgraph "NETLINK_KOBJECT_UEVENT" + direction LR + + kobject["github.com/mdlayher/kobject"] + click kobject "https://github.com/mdlayher/kobject" + end + + subgraph "NETLINK_NETFILTER" + direction LR + + go-conntrack["github.com/florianl/go-conntrack"] + click go-conntrack "https://github.com/florianl/go-conntrack" + + go-nflog["github.com/florianl/go-nflog"] + click go-nflog "https://github.com/florianl/go-nflog" + + go-nfqueue["github.com/florianl/go-nfqueue"] + click go-nfqueue "https://github.com/florianl/go-nfqueue" + + netfilter["github.com/ti-mo/netfilter"] + click netfilter "https://github.com/ti-mo/netfilter" + + nftables["github.com/google/nftables"] + click nftables "https://github.com/google/nftables" + + conntrack["github.com/ti-mo/conntrack"] + click conntrack "https://github.com/ti-mo/conntrack" + + conntrack --> netfilter + end + + subgraph "NETLINK_ROUTE" + direction LR + + go-tc["github.com/florianl/go-tc"] + click go-tc "https://github.com/florianl/go-tc" + + qdisc["github.com/ema/qdisc"] + click qdisc "https://github.com/ema/qdisc" + + rtnetlink["github.com/jsimonetti/rtnetlink"] + click rtnetlink "https://github.com/jsimonetti/rtnetlink" + + rtnl["gitlab.com/mergetb/tech/rtnl"] + click rtnl "https://gitlab.com/mergetb/tech/rtnl" + end + + subgraph "NETLINK_W1" + direction LR + + go-onewire["github.com/SpComb/go-onewire"] + click go-onewire "https://github.com/SpComb/go-onewire" + end + + subgraph "NETLINK_SOCK_DIAG" + direction LR + + go-diag["github.com/florianl/go-diag"] + click go-diag "https://github.com/florianl/go-diag" + end + + NETLINK_CONNECTOR --> netlink + NETLINK_CRYPTO --> netlink + NETLINK_GENERIC --> netlink + NETLINK_KOBJECT_UEVENT --> netlink + NETLINK_NETFILTER --> netlink + NETLINK_ROUTE --> netlink + NETLINK_SOCK_DIAG --> netlink + NETLINK_W1 --> netlink +``` diff --git a/vendor/github.com/mdlayher/netlink/align.go b/vendor/github.com/mdlayher/netlink/align.go new file mode 100644 index 000000000..4060df040 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/align.go @@ -0,0 +1,37 @@ +package netlink + +import "unsafe" + +// Functions and values used to properly align netlink messages, headers, +// and attributes. Definitions taken from Linux kernel source. + +// #define NLMSG_ALIGNTO 4U +const nlmsgAlignTo = 4 + +// #define NLMSG_ALIGN(length) ( ((length)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +func nlmsgAlign(length int) int { + return ((length) + nlmsgAlignTo - 1) & ^(nlmsgAlignTo - 1) +} + +// #define NLMSG_LENGTH(length) ((length) + NLMSG_HDRLEN) +func nlmsgLength(length int) int { + return length + nlmsgHeaderLen +} + +// #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +var nlmsgHeaderLen = nlmsgAlign(int(unsafe.Sizeof(Header{}))) + +// #define NLA_ALIGNTO 4 +const nlaAlignTo = 4 + +// #define NLA_ALIGN(length) (((length) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +func nlaAlign(length int) int { + return ((length) + nlaAlignTo - 1) & ^(nlaAlignTo - 1) +} + +// Because this package's Attribute type contains a byte slice, unsafe.Sizeof +// can't be used to determine the correct length. +const sizeofAttribute = 4 + +// #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) +var nlaHeaderLen = nlaAlign(sizeofAttribute) diff --git a/vendor/github.com/mdlayher/netlink/attribute.go b/vendor/github.com/mdlayher/netlink/attribute.go new file mode 100644 index 000000000..ebc0b2005 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/attribute.go @@ -0,0 +1,706 @@ +package netlink + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + + "github.com/mdlayher/netlink/nlenc" +) + +// errInvalidAttribute specifies if an Attribute's length is incorrect. +var errInvalidAttribute = errors.New("invalid attribute; length too short or too large") + +// An Attribute is a netlink attribute. Attributes are packed and unpacked +// to and from the Data field of Message for some netlink families. +type Attribute struct { + // Length of an Attribute, including this field and Type. + Length uint16 + + // The type of this Attribute, typically matched to a constant. Note that + // flags such as Nested and NetByteOrder must be handled manually when + // working with Attribute structures directly. + Type uint16 + + // An arbitrary payload which is specified by Type. + Data []byte +} + +// marshal marshals the contents of a into b and returns the number of bytes +// written to b, including attribute alignment padding. +func (a *Attribute) marshal(b []byte) (int, error) { + if int(a.Length) < nlaHeaderLen { + return 0, errInvalidAttribute + } + + binary.NativeEndian.PutUint16(b[0:], a.Length) + binary.NativeEndian.PutUint16(b[2:], a.Type) + n := copy(b[nlaHeaderLen:], a.Data) + + return nlaHeaderLen + nlaAlign(n), nil +} + +// unmarshal unmarshals the contents of a byte slice into an Attribute. +func (a *Attribute) unmarshal(b []byte) error { + if len(b) < nlaHeaderLen { + return errInvalidAttribute + } + + a.Length = binary.NativeEndian.Uint16(b[0:]) + a.Type = binary.NativeEndian.Uint16(b[2:]) + + if int(a.Length) > len(b) { + return errInvalidAttribute + } + + switch { + // No length, no data + case a.Length == 0: + a.Data = make([]byte, 0) + // Not enough length for any data + case int(a.Length) < nlaHeaderLen: + return errInvalidAttribute + // Data present + case int(a.Length) >= nlaHeaderLen: + a.Data = make([]byte, len(b[nlaHeaderLen:a.Length])) + copy(a.Data, b[nlaHeaderLen:a.Length]) + } + + return nil +} + +// MarshalAttributes packs a slice of Attributes into a single byte slice. +// In most cases, the Length field of each Attribute should be set to 0, so it +// can be calculated and populated automatically for each Attribute. +// +// It is recommend to use the AttributeEncoder type where possible instead of +// calling MarshalAttributes and using package nlenc functions directly. +func MarshalAttributes(attrs []Attribute) ([]byte, error) { + // Count how many bytes we should allocate to store each attribute's contents. + var c int + for _, a := range attrs { + c += nlaHeaderLen + nlaAlign(len(a.Data)) + } + + // Advance through b with idx to place attribute data at the correct offset. + var idx int + b := make([]byte, c) + for _, a := range attrs { + // Infer the length of attribute if zero. + if a.Length == 0 { + a.Length = uint16(nlaHeaderLen + len(a.Data)) + } + + // Marshal a into b and advance idx to show many bytes are occupied. + n, err := a.marshal(b[idx:]) + if err != nil { + return nil, err + } + idx += n + } + + return b, nil +} + +// UnmarshalAttributes unpacks a slice of Attributes from a single byte slice. +// +// It is recommend to use the AttributeDecoder type where possible instead of calling +// UnmarshalAttributes and using package nlenc functions directly. +func UnmarshalAttributes(b []byte) ([]Attribute, error) { + ad, err := NewAttributeDecoder(b) + if err != nil { + return nil, err + } + + // Return a nil slice when there are no attributes to decode. + if ad.Len() == 0 { + return nil, nil + } + + attrs := make([]Attribute, 0, ad.Len()) + + for ad.Next() { + if ad.a.Length != 0 { + attrs = append(attrs, ad.a) + } + } + + if err := ad.Err(); err != nil { + return nil, err + } + + return attrs, nil +} + +// An AttributeDecoder provides a safe, iterator-like, API around attribute +// decoding. +// +// It is recommend to use an AttributeDecoder where possible instead of calling +// UnmarshalAttributes and using package nlenc functions directly. +// +// The Err method must be called after the Next method returns false to determine +// if any errors occurred during iteration. +type AttributeDecoder struct { + // ByteOrder defines a specific byte order to use when processing integer + // attributes. ByteOrder should be set immediately after creating the + // AttributeDecoder: before any attributes are parsed. + // + // If not set, the native byte order will be used. + ByteOrder binary.ByteOrder + + // The current attribute being worked on. + a Attribute + + // The slice of input bytes and its iterator index. + b []byte + i int + + length int + + // Any error encountered while decoding attributes. + err error +} + +// NewAttributeDecoder creates an AttributeDecoder that unpacks Attributes +// from b and prepares the decoder for iteration. +func NewAttributeDecoder(b []byte) (*AttributeDecoder, error) { + ad := &AttributeDecoder{ + // By default, use native byte order. + ByteOrder: binary.NativeEndian, + + b: b, + } + + var err error + ad.length, err = ad.available() + if err != nil { + return nil, err + } + + return ad, nil +} + +// Next advances the decoder to the next netlink attribute. It returns false +// when no more attributes are present, or an error was encountered. +func (ad *AttributeDecoder) Next() bool { + if ad.err != nil { + // Hit an error, stop iteration. + return false + } + + // Exit if array pointer is at or beyond the end of the slice. + if ad.i >= len(ad.b) { + return false + } + + if err := ad.a.unmarshal(ad.b[ad.i:]); err != nil { + ad.err = err + return false + } + + // Advance the pointer by at least one header's length. + if int(ad.a.Length) < nlaHeaderLen { + ad.i += nlaHeaderLen + } else { + ad.i += nlaAlign(int(ad.a.Length)) + } + + return true +} + +// Type returns the Attribute.Type field of the current netlink attribute +// pointed to by the decoder. +// +// Type masks off the high bits of the netlink attribute type which may contain +// the Nested and NetByteOrder flags. These can be obtained by calling TypeFlags. +func (ad *AttributeDecoder) Type() uint16 { + // Mask off any flags stored in the high bits. + return ad.a.Type & attrTypeMask +} + +// TypeFlags returns the two high bits of the Attribute.Type field of the current +// netlink attribute pointed to by the decoder. +// +// These bits of the netlink attribute type are used for the Nested and NetByteOrder +// flags, available as the Nested and NetByteOrder constants in this package. +func (ad *AttributeDecoder) TypeFlags() uint16 { + return ad.a.Type & ^attrTypeMask +} + +// Len returns the number of netlink attributes pointed to by the decoder. +func (ad *AttributeDecoder) Len() int { return ad.length } + +// count scans the input slice to count the number of netlink attributes +// that could be decoded by Next(). +func (ad *AttributeDecoder) available() (int, error) { + var count int + for i := 0; i < len(ad.b); { + // Make sure there's at least a header's worth + // of data to read on each iteration. + if len(ad.b[i:]) < nlaHeaderLen { + return 0, errInvalidAttribute + } + + // Extract the length of the attribute. + l := int(binary.NativeEndian.Uint16(ad.b[i:])) + + // Ignore zero-length attributes. + if l != 0 { + count++ + } + + // Advance by at least a header's worth of bytes. + if l < nlaHeaderLen { + l = nlaHeaderLen + } + + i += nlaAlign(l) + } + + return count, nil +} + +// data returns the Data field of the current Attribute pointed to by the decoder. +func (ad *AttributeDecoder) data() []byte { return ad.a.Data } + +// Err returns the first error encountered by the decoder. +func (ad *AttributeDecoder) Err() error { return ad.err } + +// Bytes returns the raw bytes of the current Attribute's data. +func (ad *AttributeDecoder) Bytes() []byte { + src := ad.data() + dest := make([]byte, len(src)) + copy(dest, src) + return dest +} + +// String returns the string representation of the current Attribute's data. +func (ad *AttributeDecoder) String() string { + if ad.err != nil { + return "" + } + + return nlenc.String(ad.data()) +} + +// Uint8 returns the uint8 representation of the current Attribute's data. +func (ad *AttributeDecoder) Uint8() uint8 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 1 { + ad.err = fmt.Errorf("netlink: attribute %d is not a uint8; length: %d", ad.Type(), len(b)) + return 0 + } + + return uint8(b[0]) +} + +// Uint16 returns the uint16 representation of the current Attribute's data. +func (ad *AttributeDecoder) Uint16() uint16 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 2 { + ad.err = fmt.Errorf("netlink: attribute %d is not a uint16; length: %d", ad.Type(), len(b)) + return 0 + } + + return ad.ByteOrder.Uint16(b) +} + +// Uint32 returns the uint32 representation of the current Attribute's data. +func (ad *AttributeDecoder) Uint32() uint32 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 4 { + ad.err = fmt.Errorf("netlink: attribute %d is not a uint32; length: %d", ad.Type(), len(b)) + return 0 + } + + return ad.ByteOrder.Uint32(b) +} + +// Uint64 returns the uint64 representation of the current Attribute's data. +func (ad *AttributeDecoder) Uint64() uint64 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 8 { + ad.err = fmt.Errorf("netlink: attribute %d is not a uint64; length: %d", ad.Type(), len(b)) + return 0 + } + + return ad.ByteOrder.Uint64(b) +} + +// Int8 returns the Int8 representation of the current Attribute's data. +func (ad *AttributeDecoder) Int8() int8 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 1 { + ad.err = fmt.Errorf("netlink: attribute %d is not a int8; length: %d", ad.Type(), len(b)) + return 0 + } + + return int8(b[0]) +} + +// Int16 returns the Int16 representation of the current Attribute's data. +func (ad *AttributeDecoder) Int16() int16 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 2 { + ad.err = fmt.Errorf("netlink: attribute %d is not a int16; length: %d", ad.Type(), len(b)) + return 0 + } + + return int16(ad.ByteOrder.Uint16(b)) +} + +// Int32 returns the Int32 representation of the current Attribute's data. +func (ad *AttributeDecoder) Int32() int32 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 4 { + ad.err = fmt.Errorf("netlink: attribute %d is not a int32; length: %d", ad.Type(), len(b)) + return 0 + } + + return int32(ad.ByteOrder.Uint32(b)) +} + +// Int64 returns the Int64 representation of the current Attribute's data. +func (ad *AttributeDecoder) Int64() int64 { + if ad.err != nil { + return 0 + } + + b := ad.data() + if len(b) != 8 { + ad.err = fmt.Errorf("netlink: attribute %d is not a int64; length: %d", ad.Type(), len(b)) + return 0 + } + + return int64(ad.ByteOrder.Uint64(b)) +} + +// Flag returns a boolean representing the Attribute. +func (ad *AttributeDecoder) Flag() bool { + if ad.err != nil { + return false + } + + b := ad.data() + if len(b) != 0 { + ad.err = fmt.Errorf("netlink: attribute %d is not a flag; length: %d", ad.Type(), len(b)) + return false + } + + return true +} + +// Do is a general purpose function which allows access to the current data +// pointed to by the AttributeDecoder. +// +// Do can be used to allow parsing arbitrary data within the context of the +// decoder. Do is most useful when dealing with nested attributes, attribute +// arrays, or decoding arbitrary types (such as C structures) which don't fit +// cleanly into a typical unsigned integer value. +// +// The function fn should not retain any reference to the data b outside of the +// scope of the function. +func (ad *AttributeDecoder) Do(fn func(b []byte) error) { + if ad.err != nil { + return + } + + b := ad.data() + if err := fn(b); err != nil { + ad.err = err + } +} + +// Nested decodes data into a nested AttributeDecoder to handle nested netlink +// attributes. When calling Nested, the Err method does not need to be called on +// the nested AttributeDecoder. +// +// The nested AttributeDecoder nad inherits the same ByteOrder setting as the +// top-level AttributeDecoder ad. +func (ad *AttributeDecoder) Nested(fn func(nad *AttributeDecoder) error) { + // Because we are wrapping Do, there is no need to check ad.err immediately. + ad.Do(func(b []byte) error { + nad, err := NewAttributeDecoder(b) + if err != nil { + return err + } + nad.ByteOrder = ad.ByteOrder + + if err := fn(nad); err != nil { + return err + } + + return nad.Err() + }) +} + +// An AttributeEncoder provides a safe way to encode attributes. +// +// It is recommended to use an AttributeEncoder where possible instead of +// calling MarshalAttributes or using package nlenc directly. +// +// Errors from intermediate encoding steps are returned in the call to +// Encode. +type AttributeEncoder struct { + // ByteOrder defines a specific byte order to use when processing integer + // attributes. ByteOrder should be set immediately after creating the + // AttributeEncoder: before any attributes are encoded. + // + // If not set, the native byte order will be used. + ByteOrder binary.ByteOrder + + attrs []Attribute + err error +} + +// NewAttributeEncoder creates an AttributeEncoder that encodes Attributes. +func NewAttributeEncoder() *AttributeEncoder { + return &AttributeEncoder{ByteOrder: binary.NativeEndian} +} + +// Uint8 encodes uint8 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Uint8(typ uint16, v uint8) { + if ae.err != nil { + return + } + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: []byte{v}, + }) +} + +// Uint16 encodes uint16 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Uint16(typ uint16, v uint16) { + if ae.err != nil { + return + } + + b := make([]byte, 2) + ae.ByteOrder.PutUint16(b, v) + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Uint32 encodes uint32 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Uint32(typ uint16, v uint32) { + if ae.err != nil { + return + } + + b := make([]byte, 4) + ae.ByteOrder.PutUint32(b, v) + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Uint64 encodes uint64 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Uint64(typ uint16, v uint64) { + if ae.err != nil { + return + } + + b := make([]byte, 8) + ae.ByteOrder.PutUint64(b, v) + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Int8 encodes int8 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Int8(typ uint16, v int8) { + if ae.err != nil { + return + } + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: []byte{uint8(v)}, + }) +} + +// Int16 encodes int16 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Int16(typ uint16, v int16) { + if ae.err != nil { + return + } + + b := make([]byte, 2) + ae.ByteOrder.PutUint16(b, uint16(v)) + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Int32 encodes int32 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Int32(typ uint16, v int32) { + if ae.err != nil { + return + } + + b := make([]byte, 4) + ae.ByteOrder.PutUint32(b, uint32(v)) + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Int64 encodes int64 data into an Attribute specified by typ. +func (ae *AttributeEncoder) Int64(typ uint16, v int64) { + if ae.err != nil { + return + } + + b := make([]byte, 8) + ae.ByteOrder.PutUint64(b, uint64(v)) + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Flag encodes a flag into an Attribute specified by typ. +func (ae *AttributeEncoder) Flag(typ uint16, v bool) { + // Only set flag on no previous error or v == true. + if ae.err != nil || !v { + return + } + + // Flags have no length or data fields. + ae.attrs = append(ae.attrs, Attribute{Type: typ}) +} + +// String encodes string s as a null-terminated string into an Attribute +// specified by typ. +func (ae *AttributeEncoder) String(typ uint16, s string) { + if ae.err != nil { + return + } + + // Length checking, thanks ubiquitousbyte on GitHub. + if len(s) > math.MaxUint16-nlaHeaderLen { + ae.err = errors.New("string is too large to fit in a netlink attribute") + return + } + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: nlenc.Bytes(s), + }) +} + +// Bytes embeds raw byte data into an Attribute specified by typ. +func (ae *AttributeEncoder) Bytes(typ uint16, b []byte) { + if ae.err != nil { + return + } + + if len(b) > math.MaxUint16-nlaHeaderLen { + ae.err = errors.New("byte slice is too large to fit in a netlink attribute") + return + } + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Do is a general purpose function to encode arbitrary data into an attribute +// specified by typ. +// +// Do is especially helpful in encoding nested attributes, attribute arrays, +// or encoding arbitrary types (such as C structures) which don't fit cleanly +// into an unsigned integer value. +func (ae *AttributeEncoder) Do(typ uint16, fn func() ([]byte, error)) { + if ae.err != nil { + return + } + + b, err := fn() + if err != nil { + ae.err = err + return + } + + if len(b) > math.MaxUint16-nlaHeaderLen { + ae.err = errors.New("byte slice produced by Do is too large to fit in a netlink attribute") + return + } + + ae.attrs = append(ae.attrs, Attribute{ + Type: typ, + Data: b, + }) +} + +// Nested embeds data produced by a nested AttributeEncoder and flags that data +// with the Nested flag. When calling Nested, the Encode method should not be +// called on the nested AttributeEncoder. +// +// The nested AttributeEncoder nae inherits the same ByteOrder setting as the +// top-level AttributeEncoder ae. +func (ae *AttributeEncoder) Nested(typ uint16, fn func(nae *AttributeEncoder) error) { + // Because we are wrapping Do, there is no need to check ae.err immediately. + ae.Do(Nested|typ, func() ([]byte, error) { + nae := NewAttributeEncoder() + nae.ByteOrder = ae.ByteOrder + + if err := fn(nae); err != nil { + return nil, err + } + + return nae.Encode() + }) +} + +// Encode returns the encoded bytes representing the attributes. +func (ae *AttributeEncoder) Encode() ([]byte, error) { + if ae.err != nil { + return nil, ae.err + } + + return MarshalAttributes(ae.attrs) +} diff --git a/vendor/github.com/mdlayher/netlink/conn.go b/vendor/github.com/mdlayher/netlink/conn.go new file mode 100644 index 000000000..123081ed0 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/conn.go @@ -0,0 +1,705 @@ +package netlink + +import ( + "iter" + "math/rand" + "sync" + "sync/atomic" + "syscall" + "time" + + "golang.org/x/net/bpf" +) + +// A Conn is a connection to netlink. A Conn can be used to send and +// receives messages to and from netlink. +// +// A Conn is safe for concurrent use, but to avoid contention in +// high-throughput applications, the caller should almost certainly create a +// pool of Conns and distribute them among workers. +// +// A Conn is capable of manipulating netlink subsystems from within a specific +// Linux network namespace, but special care must be taken when doing so. See +// the documentation of Config for details. +type Conn struct { + // Atomics must come first. + // + // seq is an atomically incremented integer used to provide sequence + // numbers when Conn.Send is called. + seq uint32 + + // mu serializes access to the netlink socket for the request/response + // transaction within Execute. + mu sync.RWMutex + + // receiveMu serializes concurrent Receive and ReceiveIter calls to prevent + // races in multi-part message handling and the peek/allocate logic in + // conn_linux.go. It is separate from mu so that Send can proceed + // concurrently with Receive. + receiveMu sync.Mutex + + // sock is the operating system-specific implementation of + // a netlink sockets connection. + sock Socket + + // pid is the PID assigned by netlink. + pid uint32 + + // d provides debugging capabilities for a Conn if not nil. + d *debugger +} + +// A Socket is an operating-system specific implementation of netlink +// sockets used by Conn. +// +// Deprecated: the intent of Socket was to provide an abstraction layer for +// testing, but this abstraction is awkward to use properly and disables much of +// the functionality of the Conn type. Do not use. +type Socket interface { + Close() error + Send(m Message) error + SendMessages(m []Message) error + Receive() ([]Message, error) + ReceiveIter() iter.Seq2[Message, error] +} + +// Dial dials a connection to netlink, using the specified netlink family. +// Config specifies optional configuration for Conn. If config is nil, a default +// configuration will be used. +func Dial(family int, config *Config) (*Conn, error) { + // TODO(mdlayher): plumb in netlink.OpError wrapping? + + // Use OS-specific dial() to create Socket. + c, pid, err := dial(family, config) + if err != nil { + return nil, err + } + + return NewConn(c, pid), nil +} + +// NewConn creates a Conn using the specified Socket and PID for netlink +// communications. +// +// NewConn is primarily useful for tests. Most applications should use +// Dial instead. +func NewConn(sock Socket, pid uint32) *Conn { + // Seed the sequence number using a random number generator. + r := rand.New(rand.NewSource(time.Now().UnixNano())) + seq := r.Uint32() + + // Configure a debugger if arguments are set. + var d *debugger + if len(debugArgs) > 0 { + d = newDebugger(debugArgs) + } + + return &Conn{ + seq: seq, + sock: sock, + pid: pid, + d: d, + } +} + +// debug executes fn with the debugger if the debugger is not nil. +func (c *Conn) debug(fn func(d *debugger)) { + if c.d == nil { + return + } + + fn(c.d) +} + +// Close closes the connection and unblocks any pending read operations. +func (c *Conn) Close() error { + // Close does not acquire a lock because it must be able to interrupt any + // blocked system calls, such as when Receive is waiting on a multicast + // group message. + // + // We rely on the kernel to deal with concurrent operations to the netlink + // socket itself. + return newOpError("close", c.sock.Close()) +} + +// Execute sends a single Message to netlink using Send, receives one or more +// replies using Receive, and then checks the validity of the replies against +// the request using Validate. +// +// Execute acquires a lock for the duration of the function call which blocks +// concurrent calls to Send, SendMessages, and Receive, in order to ensure +// consistency between netlink request/reply messages. +// +// See the documentation of Send, Receive, and Validate for details about +// each function. +func (c *Conn) Execute(m Message) ([]Message, error) { + // Acquire the write lock and invoke the internal implementations of Send + // and Receive which require the lock already be held. + c.mu.Lock() + defer c.mu.Unlock() + + req, err := c.lockedSend(m) + if err != nil { + return nil, err + } + + res, err := c.lockedReceive() + if err != nil { + return nil, err + } + + if err := Validate(req, res); err != nil { + return nil, err + } + + return res, nil +} + +// SendMessages sends multiple Messages to netlink. The handling of +// a Header's Length, Sequence and PID fields is the same as when +// calling Send. +func (c *Conn) SendMessages(msgs []Message) ([]Message, error) { + // Wait for any concurrent calls to Execute to finish before proceeding. + c.mu.RLock() + defer c.mu.RUnlock() + + for i := range msgs { + c.fixMsg(&msgs[i], nlmsgLength(len(msgs[i].Data))) + } + + c.debug(func(d *debugger) { + for _, m := range msgs { + d.debugf(1, "send msgs: %+v", m) + } + }) + + if err := c.sock.SendMessages(msgs); err != nil { + c.debug(func(d *debugger) { + d.debugf(1, "send msgs: err: %v", err) + }) + + return nil, newOpError("send-messages", err) + } + + return msgs, nil +} + +// Send sends a single Message to netlink. In most cases, a Header's Length, +// Sequence, and PID fields should be set to 0, so they can be populated +// automatically before the Message is sent. On success, Send returns a copy +// of the Message with all parameters populated, for later validation. +// +// If Header.Length is 0, it will be automatically populated using the +// correct length for the Message, including its payload. +// +// If Header.Sequence is 0, it will be automatically populated using the +// next sequence number for this connection. +// +// If Header.PID is 0, it will be automatically populated using a PID +// assigned by netlink. +func (c *Conn) Send(m Message) (Message, error) { + // Wait for any concurrent calls to Execute to finish before proceeding. + c.mu.RLock() + defer c.mu.RUnlock() + + return c.lockedSend(m) +} + +// lockedSend implements Send, but must be called with c.mu acquired for reading. +// We rely on the kernel to deal with concurrent reads and writes to the netlink +// socket itself. +func (c *Conn) lockedSend(m Message) (Message, error) { + c.fixMsg(&m, nlmsgLength(len(m.Data))) + + c.debug(func(d *debugger) { + d.debugf(1, "send: %+v", m) + }) + + if err := c.sock.Send(m); err != nil { + c.debug(func(d *debugger) { + d.debugf(1, "send: err: %v", err) + }) + + return Message{}, newOpError("send", err) + } + + return m, nil +} + +// Receive receives one or more messages from netlink. Multi-part messages are +// handled transparently and returned as a single slice of Messages, with the +// final empty "multi-part done" message removed. +// +// If any of the messages indicate a netlink error, that error will be returned. +func (c *Conn) Receive() ([]Message, error) { + // Wait for any concurrent calls to Execute to finish before proceeding. + c.mu.RLock() + defer c.mu.RUnlock() + + // Serialize concurrent Receive calls. See receiveMu for details. + c.receiveMu.Lock() + defer c.receiveMu.Unlock() + + return c.lockedReceive() +} + +// ReceiveIter returns an iterator which can be used to receive messages from +// netlink. Just like Receive, multi-part messages are handled transparently and +// netlink errors are returned as errors from the iterator. +// +// If the iteration is stopped before all messages have been read and the +// response is multi-part, the remaining messages will be discarded. +func (c *Conn) ReceiveIter() iter.Seq2[Message, error] { + return func(yield func(Message, error) bool) { + // Wait for any concurrent calls to Execute to finish before proceeding. + c.mu.RLock() + defer c.mu.RUnlock() + + // Serialize concurrent ReceiveIter calls. See receiveMu for details. + c.receiveMu.Lock() + defer c.receiveMu.Unlock() + + for msg, err := range c.lockedReceiveIter() { + if err != nil { + c.debug(func(d *debugger) { + d.debugf(1, "recv: err: %v", err) + }) + yield(Message{}, err) + return + } + + c.debug(func(d *debugger) { + d.debugf(1, "recv: %+v", msg) + }) + if !yield(msg, nil) { + return + } + } + } +} + +// lockedReceive implements Receive, but must be called with c.mu acquired for reading. +// We rely on the kernel to deal with concurrent reads and writes to the netlink +// socket itself. +func (c *Conn) lockedReceive() ([]Message, error) { + var msgs []Message + + for m, err := range c.lockedReceiveIter() { + if err != nil { + c.debug(func(d *debugger) { + d.debugf(1, "recv: err: %v", err) + }) + return nil, err + } + + c.debug(func(d *debugger) { + d.debugf(1, "recv: %+v", m) + }) + + msgs = append(msgs, m) + } + + return msgs, nil +} + +// lockedReceiveIter returns an iterator which can be used to receive messages +// from netlink, but must be called with c.mu acquired for the duration of the +// iteration. +func (c *Conn) lockedReceiveIter() iter.Seq2[Message, error] { + return func(yield func(Message, error) bool) { + // NB: All non-nil errors returned from this function *must* be of type + // OpError in order to maintain the appropriate contract with callers of + // this package. + // + // This contract also applies to functions called within this function, + // such as checkMessage. + + var more, stopped bool + // send is a helper function to prevent yielding messages after the user + // has stopped iterating + var send = func(m Message, err error) { + if stopped { + return + } + if !yield(m, err) { + stopped = true + } + } + + for { + for m, err := range c.sock.ReceiveIter() { + if err != nil { + send(Message{}, newOpError("receive", err)) + return + } + + if err := checkMessage(m); err != nil { + send(Message{}, err) + return + } + + // Exit early if we encounter a multi-part done message. + // This should be safe to do since messages of type Done should always + // be the last message in a datagram. + if m.Header.Type == Done && m.Header.Flags&Multi != 0 { + return + } + + if m.Header.Flags&Multi != 0 { + more = true + } + + send(m, nil) + if stopped && !more { + // The user has stopped iterating and there are no more messages + // to read. + return + } + } + + if !more { + return + } + } + } +} + +// A groupJoinLeaver is a Socket that supports joining and leaving +// netlink multicast groups. +type groupJoinLeaver interface { + Socket + JoinGroup(group uint32) error + LeaveGroup(group uint32) error +} + +// JoinGroup joins a netlink multicast group by its ID. +func (c *Conn) JoinGroup(group uint32) error { + conn, ok := c.sock.(groupJoinLeaver) + if !ok { + return notSupported("join-group") + } + + return newOpError("join-group", conn.JoinGroup(group)) +} + +// LeaveGroup leaves a netlink multicast group by its ID. +func (c *Conn) LeaveGroup(group uint32) error { + conn, ok := c.sock.(groupJoinLeaver) + if !ok { + return notSupported("leave-group") + } + + return newOpError("leave-group", conn.LeaveGroup(group)) +} + +// A bpfSetter is a Socket that supports setting and removing BPF filters. +type bpfSetter interface { + Socket + bpf.Setter + RemoveBPF() error +} + +// SetBPF attaches an assembled BPF program to a Conn. +func (c *Conn) SetBPF(filter []bpf.RawInstruction) error { + conn, ok := c.sock.(bpfSetter) + if !ok { + return notSupported("set-bpf") + } + + return newOpError("set-bpf", conn.SetBPF(filter)) +} + +// RemoveBPF removes a BPF filter from a Conn. +func (c *Conn) RemoveBPF() error { + conn, ok := c.sock.(bpfSetter) + if !ok { + return notSupported("remove-bpf") + } + + return newOpError("remove-bpf", conn.RemoveBPF()) +} + +// A deadlineSetter is a Socket that supports setting deadlines. +type deadlineSetter interface { + Socket + SetDeadline(time.Time) error + SetReadDeadline(time.Time) error + SetWriteDeadline(time.Time) error +} + +// SetDeadline sets the read and write deadlines associated with the connection. +func (c *Conn) SetDeadline(t time.Time) error { + conn, ok := c.sock.(deadlineSetter) + if !ok { + return notSupported("set-deadline") + } + + return newOpError("set-deadline", conn.SetDeadline(t)) +} + +// SetReadDeadline sets the read deadline associated with the connection. +func (c *Conn) SetReadDeadline(t time.Time) error { + conn, ok := c.sock.(deadlineSetter) + if !ok { + return notSupported("set-read-deadline") + } + + return newOpError("set-read-deadline", conn.SetReadDeadline(t)) +} + +// SetWriteDeadline sets the write deadline associated with the connection. +func (c *Conn) SetWriteDeadline(t time.Time) error { + conn, ok := c.sock.(deadlineSetter) + if !ok { + return notSupported("set-write-deadline") + } + + return newOpError("set-write-deadline", conn.SetWriteDeadline(t)) +} + +// A ConnOption is a boolean option that may be set for a Conn. +type ConnOption int + +// Possible ConnOption values. These constants are equivalent to the Linux +// setsockopt boolean options for netlink sockets. +const ( + PacketInfo ConnOption = iota + BroadcastError + NoENOBUFS + ListenAllNSID + CapAcknowledge + ExtendedAcknowledge + GetStrictCheck +) + +// An optionSetter is a Socket that supports setting netlink options. +type optionSetter interface { + Socket + SetOption(option ConnOption, enable bool) error +} + +// SetOption enables or disables a netlink socket option for the Conn. +func (c *Conn) SetOption(option ConnOption, enable bool) error { + conn, ok := c.sock.(optionSetter) + if !ok { + return notSupported("set-option") + } + + return newOpError("set-option", conn.SetOption(option, enable)) +} + +// A bufferedSocket is a Socket that supports getting & setting connection +// buffer sizes. +type bufferedSocket interface { + Socket + SetReadBuffer(bytes int) error + SetWriteBuffer(bytes int) error + ReadBuffer() (int, error) + WriteBuffer() (int, error) +} + +// SetReadBuffer sets the size of the operating system's receive buffer +// associated with the Conn. +func (c *Conn) SetReadBuffer(bytes int) error { + conn, ok := c.sock.(bufferedSocket) + if !ok { + return notSupported("set-read-buffer") + } + + return newOpError("set-read-buffer", conn.SetReadBuffer(bytes)) +} + +// SetWriteBuffer sets the size of the operating system's transmit buffer +// associated with the Conn. +func (c *Conn) SetWriteBuffer(bytes int) error { + conn, ok := c.sock.(bufferedSocket) + if !ok { + return notSupported("set-write-buffer") + } + + return newOpError("set-write-buffer", conn.SetWriteBuffer(bytes)) +} + +// ReadBuffer reads the size of the operating system's receive buffer +// associated with the Conn. +func (c *Conn) ReadBuffer() (int, error) { + conn, ok := c.sock.(bufferedSocket) + if !ok { + return 0, notSupported("get-read-buffer") + } + + buff, err := conn.ReadBuffer() + if err != nil { + return 0, newOpError("get-read-buffer", err) + } + return buff, nil +} + +// WriteBuffer reads the size of the operating system's transmit buffer +// associated with the Conn. +func (c *Conn) WriteBuffer() (int, error) { + conn, ok := c.sock.(bufferedSocket) + if !ok { + return 0, notSupported("get-write-buffer") + } + + buff, err := conn.WriteBuffer() + if err != nil { + return 0, newOpError("get-write-buffer", err) + } + + return buff, nil +} + +// PID returns the PID associated with the Conn. It is also known as +// the port ID in netlink terminology. +// https://docs.kernel.org/userspace-api/netlink/intro.html#nlmsg-pid +func (c *Conn) PID() uint32 { + return c.pid +} + +// A syscallConner is a Socket that supports syscall.Conn. +type syscallConner interface { + Socket + syscall.Conn +} + +var _ syscall.Conn = &Conn{} + +// SyscallConn returns a raw network connection. This implements the +// syscall.Conn interface. +// +// SyscallConn is intended for advanced use cases, such as getting and setting +// arbitrary socket options using the netlink socket's file descriptor. +// +// Once invoked, it is the caller's responsibility to ensure that operations +// performed using Conn and the syscall.RawConn do not conflict with +// each other. +func (c *Conn) SyscallConn() (syscall.RawConn, error) { + sc, ok := c.sock.(syscallConner) + if !ok { + return nil, notSupported("syscall-conn") + } + + // TODO(mdlayher): mutex or similar to enforce syscall.RawConn contract of + // FD remaining valid for duration of calls? + + return sc.SyscallConn() +} + +// fixMsg updates the fields of m using the logic specified in Send. +func (c *Conn) fixMsg(m *Message, ml int) { + if m.Header.Length == 0 { + m.Header.Length = uint32(nlmsgAlign(ml)) + } + + if m.Header.Sequence == 0 { + m.Header.Sequence = c.nextSequence() + } + + if m.Header.PID == 0 { + m.Header.PID = c.pid + } +} + +// nextSequence atomically increments Conn's sequence number and returns +// the incremented value. +func (c *Conn) nextSequence() uint32 { + return atomic.AddUint32(&c.seq, 1) +} + +// Validate validates one or more reply Messages against a request Message, +// ensuring that they contain matching sequence numbers and PIDs. +func Validate(request Message, replies []Message) error { + for _, m := range replies { + // Check for mismatched sequence, unless: + // - request had no sequence, meaning we are probably validating + // a multicast reply + if m.Header.Sequence != request.Header.Sequence && request.Header.Sequence != 0 { + return newOpError("validate", errMismatchedSequence) + } + + // Check for mismatched PID, unless: + // - request had no PID, meaning we are either: + // - validating a multicast reply + // - netlink has not yet assigned us a PID + // - response had no PID, meaning it's from the kernel as a multicast reply + if m.Header.PID != request.Header.PID && request.Header.PID != 0 && m.Header.PID != 0 { + return newOpError("validate", errMismatchedPID) + } + } + + return nil +} + +// Config contains options for a Conn. +type Config struct { + // Groups is a bitmask which specifies multicast groups. If set to 0, + // no multicast group subscriptions will be made. + Groups uint32 + + // NetNS specifies the network namespace the Conn will operate in. + // + // If set (non-zero), Conn will enter the specified network namespace and + // an error will occur in Dial if the operation fails. + // + // If not set (zero), a best-effort attempt will be made to enter the + // network namespace of the calling thread: this means that any changes made + // to the calling thread's network namespace will also be reflected in Conn. + // If this operation fails (due to lack of permissions or because network + // namespaces are disabled by kernel configuration), Dial will not return + // an error, and the Conn will operate in the default network namespace of + // the process. This enables non-privileged use of Conn in applications + // which do not require elevated privileges. + // + // Entering a network namespace is a privileged operation (root or + // CAP_SYS_ADMIN are required), and most applications should leave this set + // to 0. + NetNS int + + // DisableNSLockThread is a no-op. + // + // Deprecated: internal changes have made this option obsolete and it has no + // effect. Do not use. + DisableNSLockThread bool + + // PID specifies the port ID used to bind the netlink socket. If set to 0, + // the kernel will assign a port ID on the caller's behalf. + // + // Most callers should leave this field set to 0. This option is intended + // for advanced use cases where the kernel expects a fixed unicast address + // destination for netlink messages. + PID uint32 + + // Strict applies a more strict default set of options to the Conn, + // including: + // - ExtendedAcknowledge: true + // - provides more useful error messages when supported by the kernel + // - GetStrictCheck: true + // - more strictly enforces request validation for some families such + // as rtnetlink which were historically misused + // + // If any of the options specified by Strict cannot be configured due to an + // outdated kernel or similar, an error will be returned. + // + // When possible, setting Strict to true is recommended for applications + // running on modern Linux kernels. + Strict bool + + // MessageBufferSize specifies a fixed buffer size for receiving netlink + // messages. When set, the connection will reuse a single pre-allocated + // buffer of this size instead of peeking at each message to determine + // the exact size needed. + // + // This is useful for high-throughput applications where the overhead of + // peeking at each message is undesirable and the maximum message size + // is known in advance. + // + // If set to 0 (the default), the connection will peek at the upcoming + // message before allocating a buffer for it. + // + // Note: this is not the same as the kernel socket receive buffer which + // can be configured using SetReadBuffer. MessageBufferSize only controls + // the userspace buffer passed to recvmsg. + MessageBufferSize int +} diff --git a/vendor/github.com/mdlayher/netlink/conn_linux.go b/vendor/github.com/mdlayher/netlink/conn_linux.go new file mode 100644 index 000000000..c9618f553 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/conn_linux.go @@ -0,0 +1,312 @@ +//go:build linux + +package netlink + +import ( + "context" + "iter" + "os" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/mdlayher/socket" + "golang.org/x/net/bpf" + "golang.org/x/sys/unix" +) + +var _ Socket = &conn{} + +// A conn is the Linux implementation of a netlink sockets connection. +type conn struct { + s *socket.Conn + pool *sync.Pool +} + +// dial is the entry point for Dial. dial opens a netlink socket using +// system calls, and returns its PID. +func dial(family int, config *Config) (*conn, uint32, error) { + if config == nil { + config = &Config{} + } + + // Prepare the netlink socket. + s, err := socket.Socket( + unix.AF_NETLINK, + unix.SOCK_RAW, + family, + "netlink", + &socket.Config{NetNS: config.NetNS}, + ) + if err != nil { + return nil, 0, err + } + + return newConn(s, config) +} + +// newConn binds a connection to netlink using the input *socket.Conn. +func newConn(s *socket.Conn, config *Config) (*conn, uint32, error) { + if config == nil { + config = &Config{} + } + + addr := &unix.SockaddrNetlink{ + Family: unix.AF_NETLINK, + Groups: config.Groups, + Pid: config.PID, + } + + // Socket must be closed in the event of any system call errors, to avoid + // leaking file descriptors. + + if err := s.Bind(addr); err != nil { + _ = s.Close() + return nil, 0, err + } + + sa, err := s.Getsockname() + if err != nil { + _ = s.Close() + return nil, 0, err + } + + c := &conn{s: s} + + if config.MessageBufferSize > 0 { + c.pool = &sync.Pool{ + New: func() any { + b := make([]byte, config.MessageBufferSize) + return &b + }, + } + } + + if config.Strict { + // The caller has requested the strict option set. Historically we have + // recommended checking for ENOPROTOOPT if the kernel does not support + // the option in question, but that may result in a silent failure and + // unexpected behavior for the user. + // + // Treat any error here as a fatal error, and require the caller to deal + // with it. + for _, o := range []ConnOption{ExtendedAcknowledge, GetStrictCheck} { + if err := c.SetOption(o, true); err != nil { + _ = c.Close() + return nil, 0, err + } + } + } + + return c, sa.(*unix.SockaddrNetlink).Pid, nil +} + +// SendMessages serializes multiple Messages and sends them to netlink. +func (c *conn) SendMessages(messages []Message) error { + var buf []byte + for _, m := range messages { + b, err := m.MarshalBinary() + if err != nil { + return err + } + + buf = append(buf, b...) + } + + sa := &unix.SockaddrNetlink{Family: unix.AF_NETLINK} + _, err := c.s.Sendmsg(context.Background(), buf, nil, sa, 0) + return err +} + +// Send sends a single Message to netlink. +func (c *conn) Send(m Message) error { + b, err := m.MarshalBinary() + if err != nil { + return err + } + + sa := &unix.SockaddrNetlink{Family: unix.AF_NETLINK} + _, err = c.s.Sendmsg(context.Background(), b, nil, sa, 0) + return err +} + +// Receive receives one or more Messages from netlink. +func (c *conn) Receive() ([]Message, error) { + var msgs []Message + for msg, err := range c.ReceiveIter() { + if err != nil { + return nil, err + } + msgs = append(msgs, msg) + } + + return msgs, nil +} + +// getBuffer returns the buffer to use for receiving messages and a function to +// release it back to the pool if applicable. If the pool is not configured, a +// new buffer is allocated by peeking the size of the next message to be +// received. The buffer size is aligned to the next multiple of the alignment of a netlink +// message, to avoid panic when parsing messages from the buffer. +func (c *conn) getBuffer() ([]byte, func(), error) { + if c.pool != nil { + bp := c.pool.Get().(*[]byte) + return *bp, func() { c.pool.Put(bp) }, nil + } + + n, _, _, _, err := c.s.Recvmsg(context.Background(), nil, nil, unix.MSG_PEEK|unix.MSG_TRUNC) + if err != nil { + return nil, nil, err + } + + return make([]byte, nlmsgAlign(n)), func() {}, nil +} + +// ownedBuffer trims b to the aligned size of the received datagram and, when +// using pooled buffers, copies it to memory owned by the caller. +func (c *conn) ownedBuffer(b []byte, n int) []byte { + aligned := nlmsgAlign(n) + if c.pool == nil { + return b[:aligned] + } + + // A pooled buffer may not have an aligned length. Copy the received bytes + // to an aligned buffer and let the zero-value tail bytes act as padding. + out := make([]byte, aligned) + copy(out, b[:n]) + return out +} + +// ReceiveIter returns an iterator over Messages received from netlink. +func (c *conn) ReceiveIter() iter.Seq2[Message, error] { + return func(yield func(Message, error) bool) { + b, release, err := c.getBuffer() + if err != nil { + yield(Message{}, err) + return + } + defer release() + + // Read out all available messages + // TODO(mdlayher): deal with OOB message data if available, such as + // when PacketInfo ConnOption is true. + n, _, flags, _, err := c.s.Recvmsg(context.Background(), b, nil, 0) + if err != nil { + yield(Message{}, err) + return + } + + if flags&unix.MSG_TRUNC != 0 { + // Our buffer was too small to read the entire message, + // this should not happen since we peeked above, but if it does, + // return an error. + yield(Message{}, errMessageTruncated) + return + } + + for msg, err := range parseMessagesIter(c.ownedBuffer(b, n)) { + if err != nil { + yield(Message{}, err) + return + } + + if !yield(msg, nil) { + return + } + } + } +} + +// Close closes the connection. +func (c *conn) Close() error { return c.s.Close() } + +// JoinGroup joins a multicast group by ID. +func (c *conn) JoinGroup(group uint32) error { + return c.s.SetsockoptInt(unix.SOL_NETLINK, unix.NETLINK_ADD_MEMBERSHIP, int(group)) +} + +// LeaveGroup leaves a multicast group by ID. +func (c *conn) LeaveGroup(group uint32) error { + return c.s.SetsockoptInt(unix.SOL_NETLINK, unix.NETLINK_DROP_MEMBERSHIP, int(group)) +} + +// SetBPF attaches an assembled BPF program to a conn. +func (c *conn) SetBPF(filter []bpf.RawInstruction) error { return c.s.SetBPF(filter) } + +// RemoveBPF removes a BPF filter from a conn. +func (c *conn) RemoveBPF() error { return c.s.RemoveBPF() } + +// SetOption enables or disables a netlink socket option for the Conn. +func (c *conn) SetOption(option ConnOption, enable bool) error { + o, ok := linuxOption(option) + if !ok { + // Return the typical Linux error for an unknown ConnOption. + return os.NewSyscallError("setsockopt", unix.ENOPROTOOPT) + } + + var v int + if enable { + v = 1 + } + + return c.s.SetsockoptInt(unix.SOL_NETLINK, o, v) +} + +func (c *conn) SetDeadline(t time.Time) error { return c.s.SetDeadline(t) } +func (c *conn) SetReadDeadline(t time.Time) error { return c.s.SetReadDeadline(t) } +func (c *conn) SetWriteDeadline(t time.Time) error { return c.s.SetWriteDeadline(t) } + +// SetReadBuffer sets the size of the operating system's receive buffer +// associated with the Conn. +func (c *conn) SetReadBuffer(bytes int) error { return c.s.SetReadBuffer(bytes) } + +// SetReadBuffer sets the size of the operating system's transmit buffer +// associated with the Conn. +func (c *conn) SetWriteBuffer(bytes int) error { return c.s.SetWriteBuffer(bytes) } + +// ReadBuffer reads the size of the operating system's receive buffer +// associated with the Conn. +func (c *conn) ReadBuffer() (int, error) { return c.s.ReadBuffer() } + +// WriteBuffer reads the size of the operating system's transmit buffer +// associated with the Conn. +func (c *conn) WriteBuffer() (int, error) { return c.s.WriteBuffer() } + +// SyscallConn returns a raw network connection. +func (c *conn) SyscallConn() (syscall.RawConn, error) { return c.s.SyscallConn() } + +// linuxOption converts a ConnOption to its Linux value. +func linuxOption(o ConnOption) (int, bool) { + switch o { + case PacketInfo: + return unix.NETLINK_PKTINFO, true + case BroadcastError: + return unix.NETLINK_BROADCAST_ERROR, true + case NoENOBUFS: + return unix.NETLINK_NO_ENOBUFS, true + case ListenAllNSID: + return unix.NETLINK_LISTEN_ALL_NSID, true + case CapAcknowledge: + return unix.NETLINK_CAP_ACK, true + case ExtendedAcknowledge: + return unix.NETLINK_EXT_ACK, true + case GetStrictCheck: + return unix.NETLINK_GET_STRICT_CHK, true + default: + return 0, false + } +} + +// sysToHeader converts a syscall.NlMsghdr to a Header. +func sysToHeader(r syscall.NlMsghdr) Header { + // NB: the memory layout of Header and syscall.NlMsgHdr must be + // exactly the same for this unsafe cast to work + return *(*Header)(unsafe.Pointer(&r)) +} + +// newError converts an error number from netlink into the appropriate +// system call error for Linux. +func newError(errno int) error { + return syscall.Errno(errno) +} diff --git a/vendor/github.com/mdlayher/netlink/conn_others.go b/vendor/github.com/mdlayher/netlink/conn_others.go new file mode 100644 index 000000000..5eb24c06f --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/conn_others.go @@ -0,0 +1,36 @@ +//go:build !linux +// +build !linux + +package netlink + +import ( + "fmt" + "iter" + "runtime" +) + +// errUnimplemented is returned by all functions on platforms that +// cannot make use of netlink sockets. +var errUnimplemented = fmt.Errorf("netlink: not implemented on %s/%s", + runtime.GOOS, runtime.GOARCH) + +var _ Socket = &conn{} + +// A conn is the no-op implementation of a netlink sockets connection. +type conn struct{} + +// All cross-platform functions and Socket methods are unimplemented outside +// of Linux. + +func dial(_ int, _ *Config) (*conn, uint32, error) { return nil, 0, errUnimplemented } +func newError(_ int) error { return errUnimplemented } + +func (c *conn) Send(_ Message) error { return errUnimplemented } +func (c *conn) SendMessages(_ []Message) error { return errUnimplemented } +func (c *conn) Receive() ([]Message, error) { return nil, errUnimplemented } +func (c *conn) Close() error { return errUnimplemented } +func (c *conn) ReceiveIter() iter.Seq2[Message, error] { + return func(yield func(Message, error) bool) { + yield(Message{}, errUnimplemented) + } +} diff --git a/vendor/github.com/mdlayher/netlink/debug.go b/vendor/github.com/mdlayher/netlink/debug.go new file mode 100644 index 000000000..40ad2de77 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/debug.go @@ -0,0 +1,33 @@ +package netlink + +import ( + "fmt" + "log" + "os" + "strings" +) + +// Arguments used to create a debugger. +var debugArgs []string + +func init() { + // Is netlink debugging enabled? + s := os.Getenv("NLDEBUG") + if s == "" { + return + } + + debugArgs = strings.Split(s, ",") +} + +// A debugger is used to provide debugging information about a netlink connection. +type debugger struct { + Log *log.Logger + Level int + Format string +} + +// panicf is a helper to panic with formatted text. +func panicf(format string, a ...any) { + panic(fmt.Sprintf(format, a...)) +} diff --git a/vendor/github.com/mdlayher/netlink/debug_linux.go b/vendor/github.com/mdlayher/netlink/debug_linux.go new file mode 100644 index 000000000..5dd7b2659 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/debug_linux.go @@ -0,0 +1,196 @@ +//go:build linux + +package netlink + +import ( + "encoding/binary" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// newDebugger creates a debugger by parsing key=value arguments. +func newDebugger(args []string) *debugger { + d := &debugger{ + Log: log.New(os.Stderr, "nl: ", 0), + Level: 1, + Format: "mnl", + } + for _, a := range args { + kv := strings.Split(a, "=") + if len(kv) != 2 { + continue + } + switch kv[0] { + case "level": + level, err := strconv.Atoi(kv[1]) + if err != nil { + panicf("netlink: invalid NLDEBUG level: %q", a) + } + d.Level = level + case "format": + d.Format = kv[1] + } + } + return d +} + +// debugf prints debugging information at the specified level, if d.Level is high enough to print the message. +func (d *debugger) debugf(level int, format string, v ...any) { + if d.Level < level { + return + } + switch d.Format { + case "mnl": + colorize := true + _, err := unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ) + if err != nil { + colorize = false + } + for _, iface := range v { + if msg, ok := iface.(Message); ok { + nlmsgFprintf(d.Log.Writer(), msg, colorize) + } else { + d.Log.Printf(format, v...) + } + } + default: + d.Log.Printf(format, v...) + } +} + +// nlmsgFprintfHeader prints the netlink message header to fd. +func nlmsgFprintfHeader(fd io.Writer, nlh Header) { + fmt.Fprintf(fd, "----------------\t------------------\n") + fmt.Fprintf(fd, "| %010d |\t| message length |\n", nlh.Length) + fmt.Fprintf(fd, "| %05d | %s%s%s%s |\t| type | flags |\n", + nlh.Type, + ternary(nlh.Flags&Request != 0, "R", "-"), + ternary(nlh.Flags&Multi != 0, "M", "-"), + ternary(nlh.Flags&Acknowledge != 0, "A", "-"), + ternary(nlh.Flags&Echo != 0, "E", "-"), + ) + fmt.Fprintf(fd, "| %010d |\t| sequence number|\n", nlh.Sequence) + fmt.Fprintf(fd, "| %010d |\t| port ID |\n", nlh.PID) + fmt.Fprintf(fd, "----------------\t------------------\n") +} + +// nlmsgFprintf prints a single Message for netlink errors and attributes. +func nlmsgFprintf(fd io.Writer, m Message, colorize bool) { + var hasHeader bool + nlmsgFprintfHeader(fd, m.Header) + switch { + case m.Header.Type == Error: + hasHeader = true + case m.Header.Type == Done && m.Header.Flags&Multi != 0: + if len(m.Data) == 0 { + return + } + default: + // Neither, nothing to do. + } + // Errno occupies 4 bytes. + const endErrno = 4 + if len(m.Data) < endErrno { + return + } + + c := int32(binary.NativeEndian.Uint32(m.Data[:endErrno])) + if c != 0 { + b := m.Data[0:4] + fmt.Fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff&b[0], 0xff&b[1], + 0xff&b[2], 0xff&b[3]) + fmt.Fprintf(fd, "| extra header |\n") + } + + // Flags indicate an extended acknowledgement. The type/flags combination + // checked above determines the offset where the TLVs occur. + var off int + if hasHeader { + // There is an nlmsghdr preceding the TLVs. + if len(m.Data) < endErrno+nlmsgHeaderLen { + return + } + // The TLVs should be at the offset indicated by the nlmsghdr.length, + // plus the offset where the header began. But make sure the calculated + // offset is still in-bounds. + h := *(*Header)(unsafe.Pointer(&m.Data[endErrno : endErrno+nlmsgHeaderLen][0])) + off = endErrno + int(h.Length) + if len(m.Data) < off { + return + } + } else { + // There is no nlmsghdr preceding the TLVs, parse them directly. + off = endErrno + } + + data := m.Data[off:] + for i := 0; i < len(data); { + // Make sure there's at least a header's worth of data to read on each iteration. + if len(data[i:]) < nlaHeaderLen { + break + } + // Extract the length of the attribute. + l := int(binary.NativeEndian.Uint16(data[i:])) + // extract the type + t := binary.NativeEndian.Uint16(data[i+2:]) + // print attribute header + if colorize { + fmt.Fprintf(fd, "|\033[1;31m%05d|\033[1;32m%s%s|\033[1;34m%05d\033[0m|\t", + l, + ternary(t&syscall.NLA_F_NESTED != 0, "N", "-"), + ternary(t&syscall.NLA_F_NET_BYTEORDER != 0, "B", "-"), + t&attrTypeMask) + fmt.Fprintf(fd, "|len |flags| type|\n") + } else { + fmt.Fprintf(fd, "|%05d|%s%s|%05d|\t", + l, + ternary(t&syscall.NLA_F_NESTED != 0, "N", "-"), + ternary(t&syscall.NLA_F_NET_BYTEORDER != 0, "B", "-"), + t&attrTypeMask) + fmt.Fprintf(fd, "|len |flags| type|\n") + } + + nextAttr := i + nlaAlign(l) + i += nlaHeaderLen + + // Ignore zero-length attributes. + if l == 0 { + continue + } + // If nested check the next attribute + if t&syscall.NLA_F_NESTED != 0 { + continue + } + // Print the remaining attributes bytes + for ; i < nextAttr; i += 4 { + fmt.Fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff&data[i], 0xff&data[i+1], + 0xff&data[i+2], 0xff&data[i+3]) + fmt.Fprintf(fd, "| data |") + fmt.Fprintf(fd, "\t %s %s %s %s\n", + ternary(strconv.IsPrint(rune(data[i])), string(data[i]), " "), + ternary(strconv.IsPrint(rune(data[i+1])), string(data[i+1]), " "), + ternary(strconv.IsPrint(rune(data[i+2])), string(data[i+2]), " "), + ternary(strconv.IsPrint(rune(data[i+3])), string(data[i+3]), " "), + ) + } + } + fmt.Fprintf(fd, "----------------\t------------------\n") +} + +// ternary returns iftrue if cond is true, else iffalse. +func ternary(cond bool, iftrue string, iffalse string) string { + if cond { + return iftrue + } + return iffalse +} diff --git a/vendor/github.com/mdlayher/netlink/debug_others.go b/vendor/github.com/mdlayher/netlink/debug_others.go new file mode 100644 index 000000000..8732c639b --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/debug_others.go @@ -0,0 +1,40 @@ +//go:build !linux + +package netlink + +import ( + "log" + "os" + "strconv" + "strings" +) + +// newDebugger creates a debugger by parsing key=value arguments. +func newDebugger(args []string) *debugger { + d := &debugger{ + Log: log.New(os.Stderr, "nl: ", 0), + Level: 1, + } + for _, a := range args { + kv := strings.Split(a, "=") + if len(kv) != 2 { + continue + } + switch kv[0] { + case "level": + level, err := strconv.Atoi(kv[1]) + if err != nil { + panicf("netlink: invalid NLDEBUG level: %q", a) + } + d.Level = level + } + } + return d +} + +// debugf prints debugging information at the specified level, if d.Level is high enough to print the message. +func (d *debugger) debugf(level int, format string, v ...interface{}) { + if d.Level >= level { + d.Log.Printf(format, v...) + } +} diff --git a/vendor/github.com/mdlayher/netlink/doc.go b/vendor/github.com/mdlayher/netlink/doc.go new file mode 100644 index 000000000..f0c51c7be --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/doc.go @@ -0,0 +1,36 @@ +// Package netlink provides low-level access to Linux netlink sockets +// (AF_NETLINK). +// +// If you have any questions or you'd like some guidance, please join us on +// Gophers Slack (https://invite.slack.golangbridge.org) in the #networking +// channel! +// +// # Network namespaces +// +// This package is aware of Linux network namespaces, and can enter different +// network namespaces either implicitly or explicitly, depending on +// configuration. The Config structure passed to Dial to create a Conn controls +// these behaviors. See the documentation of Config.NetNS for details. +// +// # Debugging +// +// This package supports rudimentary netlink connection debugging support. To +// enable this, run your binary with the NLDEBUG environment variable set. +// Debugging information will be output to stderr with a prefix of "nl:". +// +// To use the debugging defaults, use: +// +// $ NLDEBUG=1 ./nlctl +// +// To configure individual aspects of the debugger, pass key/value options such +// as: +// +// $ NLDEBUG=level=1 ./nlctl +// +// $ NLDEBUG=level=1,format=mnl ./nlctl +// +// Available key/value debugger options include: +// +// level=N: specify the debugging level (only "1" is currently supported) +// format=mnl: specify the same format used by libmnl (nft --debug=all) +package netlink diff --git a/vendor/github.com/mdlayher/netlink/errors.go b/vendor/github.com/mdlayher/netlink/errors.go new file mode 100644 index 000000000..0f1b468b1 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/errors.go @@ -0,0 +1,149 @@ +package netlink + +import ( + "errors" + "fmt" + "net" + "os" + "strings" +) + +// Error messages which can be returned by Validate. +var ( + errMismatchedSequence = errors.New("mismatched sequence in netlink reply") + errMismatchedPID = errors.New("mismatched PID in netlink reply") + errShortErrorMessage = errors.New("not enough data for netlink error code") +) + +// Errors which can be returned by a Socket that does not implement +// all exposed methods of Conn. + +var errNotSupported = errors.New("operation not supported") + +// errMessageTruncated is returned when a received message was truncated +// because the receive buffer was too small. +var errMessageTruncated = errors.New("message truncated") + +// notSupported provides a concise constructor for "not supported" errors. +func notSupported(op string) error { + return newOpError(op, errNotSupported) +} + +// IsNotExist determines if an error is produced as the result of querying some +// file, object, resource, etc. which does not exist. +// +// Deprecated: use errors.Unwrap and/or `errors.Is(err, os.Permission)` in Go +// 1.13+. +func IsNotExist(err error) bool { + switch err := err.(type) { + case *OpError: + // Unwrap the inner error and use the stdlib's logic. + return os.IsNotExist(err.Err) + default: + return os.IsNotExist(err) + } +} + +var ( + _ error = &OpError{} + _ net.Error = &OpError{} + // Ensure compatibility with Go 1.13+ errors package. + _ interface{ Unwrap() error } = &OpError{} +) + +// An OpError is an error produced as the result of a failed netlink operation. +type OpError struct { + // Op is the operation which caused this OpError, such as "send" + // or "receive". + Op string + + // Err is the underlying error which caused this OpError. + // + // If Err was produced by a system call error, Err will be of type + // *os.SyscallError. If Err was produced by an error code in a netlink + // message, Err will contain a raw error value type such as a unix.Errno. + // + // Most callers should inspect Err using errors.Is from the standard + // library. + Err error + + // Message and Offset contain additional error information provided by the + // kernel when the ExtendedAcknowledge option is set on a Conn and the + // kernel indicates the AcknowledgeTLVs flag in a response. If this option + // is not set, both of these fields will be empty. + Message string + Offset int + + // Sequence is the netlink message sequence number associated with the + // error. This field is only populated if the error was produced as the + // result of a netlink message; otherwise, it will be zero. + Sequence uint32 +} + +// newOpError is a small wrapper for creating an OpError. As a convenience, it +// returns nil if the input err is nil: akin to os.NewSyscallError. +func newOpError(op string, err error) error { + if err == nil { + return nil + } + + return &OpError{ + Op: op, + Err: err, + } +} + +func (e *OpError) Error() string { + if e == nil { + return "" + } + + var sb strings.Builder + _, _ = fmt.Fprintf(&sb, "netlink %s: %v", e.Op, e.Err) + + if e.Message != "" || e.Offset != 0 { + _, _ = fmt.Fprintf(&sb, ", offset: %d, message: %q", + e.Offset, e.Message) + } + + return sb.String() +} + +// Unwrap unwraps the internal Err field for use with errors.Unwrap. +func (e *OpError) Unwrap() error { return e.Err } + +// Portions of this code taken from the Go standard library: +// +// Copyright 2009 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. + +type timeout interface { + Timeout() bool +} + +// Timeout reports whether the error was caused by an I/O timeout. +func (e *OpError) Timeout() bool { + ne := &os.SyscallError{} + if errors.As(e.Err, &ne) { + t, ok := ne.Err.(timeout) + return ok && t.Timeout() + } + t, ok := e.Err.(timeout) + return ok && t.Timeout() +} + +type temporary interface { + Temporary() bool +} + +// Temporary reports whether an operation may succeed if retried. +func (e *OpError) Temporary() bool { + ne := &os.SyscallError{} + if errors.As(e.Err, &ne) { + t, ok := ne.Err.(temporary) + return ok && t.Temporary() + } + t, ok := e.Err.(temporary) + return ok && t.Temporary() +} diff --git a/vendor/github.com/mdlayher/netlink/fuzz.go b/vendor/github.com/mdlayher/netlink/fuzz.go new file mode 100644 index 000000000..fdd6b6498 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/fuzz.go @@ -0,0 +1,82 @@ +//go:build gofuzz +// +build gofuzz + +package netlink + +import "github.com/google/go-cmp/cmp" + +func fuzz(b1 []byte) int { + // 1. unmarshal, marshal, unmarshal again to check m1 and m2 for equality + // after a round trip. checkMessage is also used because there is a fair + // amount of tricky logic around testing for presence of error headers and + // extended acknowledgement attributes. + var m1 Message + if err := m1.UnmarshalBinary(b1); err != nil { + return 0 + } + + if err := checkMessage(m1); err != nil { + return 0 + } + + b2, err := m1.MarshalBinary() + if err != nil { + panicf("failed to marshal m1: %v", err) + } + + var m2 Message + if err := m2.UnmarshalBinary(b2); err != nil { + panicf("failed to unmarshal m2: %v", err) + } + + if err := checkMessage(m2); err != nil { + panicf("failed to check m2: %v", err) + } + + if diff := cmp.Diff(m1, m2); diff != "" { + panicf("unexpected Message (-want +got):\n%s", diff) + } + + // 2. marshal again and compare b2 and b3 (b1 may have reserved bytes set + // which we ignore and fill with zeros when marshaling) for equality. + b3, err := m2.MarshalBinary() + if err != nil { + panicf("failed to marshal m2: %v", err) + } + + if diff := cmp.Diff(b2, b3); diff != "" { + panicf("unexpected message bytes (-want +got):\n%s", diff) + } + + // 3. unmarshal any possible attributes from m1's data and marshal them + // again for comparison. + a1, err := UnmarshalAttributes(m1.Data) + if err != nil { + return 0 + } + + ab1, err := MarshalAttributes(a1) + if err != nil { + panicf("failed to marshal a1: %v", err) + } + + a2, err := UnmarshalAttributes(ab1) + if err != nil { + panicf("failed to unmarshal a2: %v", err) + } + + if diff := cmp.Diff(a1, a2); diff != "" { + panicf("unexpected Attributes (-want +got):\n%s", diff) + } + + ab2, err := MarshalAttributes(a2) + if err != nil { + panicf("failed to marshal a2: %v", err) + } + + if diff := cmp.Diff(ab1, ab2); diff != "" { + panicf("unexpected attribute bytes (-want +got):\n%s", diff) + } + + return 1 +} diff --git a/vendor/github.com/mdlayher/netlink/message.go b/vendor/github.com/mdlayher/netlink/message.go new file mode 100644 index 000000000..9ec4501c4 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/message.go @@ -0,0 +1,386 @@ +package netlink + +import ( + "encoding/binary" + "errors" + "fmt" + "iter" + "unsafe" +) + +// Flags which may apply to netlink attribute types when communicating with +// certain netlink families. +const ( + Nested uint16 = 0x8000 + NetByteOrder uint16 = 0x4000 + + // attrTypeMask masks off Type bits used for the above flags. + attrTypeMask uint16 = 0x3fff +) + +// Various errors which may occur when attempting to marshal or unmarshal +// a Message to and from its binary form. +var ( + errIncorrectMessageLength = errors.New("netlink message header length incorrect") + errShortMessage = errors.New("not enough data to create a netlink message") + errUnalignedMessage = errors.New("input data is not properly aligned for netlink message") +) + +// HeaderFlags specify flags which may be present in a Header. +type HeaderFlags uint16 + +const ( + // General netlink communication flags. + + // Request indicates a request to netlink. + Request HeaderFlags = 1 + + // Multi indicates a multi-part message, terminated by Done on the + // last message. + Multi HeaderFlags = 2 + + // Acknowledge requests that netlink reply with an acknowledgement + // using Error and, if needed, an error code. + Acknowledge HeaderFlags = 4 + + // Echo requests that netlink echo this request back to the sender. + Echo HeaderFlags = 8 + + // DumpInterrupted indicates that a dump was inconsistent due to a + // sequence change. + DumpInterrupted HeaderFlags = 16 + + // DumpFiltered indicates that a dump was filtered as requested. + DumpFiltered HeaderFlags = 32 + + // Flags used to retrieve data from netlink. + + // Root requests that netlink return a complete table instead of a + // single entry. + Root HeaderFlags = 0x100 + + // Match requests that netlink return a list of all matching entries. + Match HeaderFlags = 0x200 + + // Atomic requests that netlink send an atomic snapshot of its entries. + // Requires CAP_NET_ADMIN or an effective UID of 0. + Atomic HeaderFlags = 0x400 + + // Dump requests that netlink return a complete list of all entries. + Dump HeaderFlags = Root | Match + + // Flags used to create objects. + + // Replace indicates request replaces an existing matching object. + Replace HeaderFlags = 0x100 + + // Excl indicates request does not replace the object if it already exists. + Excl HeaderFlags = 0x200 + + // Create indicates request creates an object if it doesn't already exist. + Create HeaderFlags = 0x400 + + // Append indicates request adds to the end of the object list. + Append HeaderFlags = 0x800 + + // Flags for extended acknowledgements. + + // Capped indicates the size of a request was capped in an extended + // acknowledgement. + Capped HeaderFlags = 0x100 + + // AcknowledgeTLVs indicates the presence of netlink extended + // acknowledgement TLVs in a response. + AcknowledgeTLVs HeaderFlags = 0x200 +) + +// String returns the string representation of a HeaderFlags. +func (f HeaderFlags) String() string { + names := []string{ + "request", + "multi", + "acknowledge", + "echo", + "dumpinterrupted", + "dumpfiltered", + } + + var s string + + left := uint(f) + + for i, name := range names { + if f&(1< 0 { + if s != "" { + s += "|" + } + s += fmt.Sprintf("%#x", left) + } + + return s +} + +// HeaderType specifies the type of a Header. +type HeaderType uint16 + +const ( + // Noop indicates that no action was taken. + Noop HeaderType = 0x1 + + // Error indicates an error code is present, which is also used to indicate + // success when the code is 0. + Error HeaderType = 0x2 + + // Done indicates the end of a multi-part message. + Done HeaderType = 0x3 + + // Overrun indicates that data was lost from this message. + Overrun HeaderType = 0x4 +) + +// String returns the string representation of a HeaderType. +func (t HeaderType) String() string { + switch t { + case Noop: + return "noop" + case Error: + return "error" + case Done: + return "done" + case Overrun: + return "overrun" + default: + return fmt.Sprintf("unknown(%d)", t) + } +} + +// NB: the memory layout of Header and Linux's syscall.NlMsgHdr must be +// exactly the same. Cannot reorder, change data type, add, or remove fields. +// Named types of the same size (e.g. HeaderFlags is a uint16) are okay. + +// A Header is a netlink header. A Header is sent and received with each +// Message to indicate metadata regarding a Message. +type Header struct { + // Length of a Message, including this Header. + Length uint32 + + // Contents of a Message. + Type HeaderType + + // Flags which may be used to modify a request or response. + Flags HeaderFlags + + // The sequence number of a Message. + Sequence uint32 + + // The port ID of the sending process. + PID uint32 +} + +// A Message is a netlink message. It contains a Header and an arbitrary +// byte payload, which may be decoded using information from the Header. +// +// Data is often populated with netlink attributes. For easy encoding and +// decoding of attributes, see the AttributeDecoder and AttributeEncoder types. +type Message struct { + Header Header + Data []byte +} + +// MarshalBinary marshals a Message into a byte slice. +func (m Message) MarshalBinary() ([]byte, error) { + ml := nlmsgAlign(int(m.Header.Length)) + if ml < nlmsgHeaderLen || ml != int(m.Header.Length) { + return nil, errIncorrectMessageLength + } + + b := make([]byte, ml) + + binary.NativeEndian.PutUint32(b[0:], m.Header.Length) + binary.NativeEndian.PutUint16(b[4:], uint16(m.Header.Type)) + binary.NativeEndian.PutUint16(b[6:], uint16(m.Header.Flags)) + binary.NativeEndian.PutUint32(b[8:], m.Header.Sequence) + binary.NativeEndian.PutUint32(b[12:], m.Header.PID) + copy(b[16:], m.Data) + + return b, nil +} + +// UnmarshalBinary unmarshals the contents of a byte slice into a Message. +func (m *Message) UnmarshalBinary(b []byte) error { + if len(b) < nlmsgHeaderLen { + return errShortMessage + } + if len(b) != nlmsgAlign(len(b)) { + return errUnalignedMessage + } + + // Don't allow misleading length + m.Header.Length = binary.NativeEndian.Uint32(b[0:]) + if int(m.Header.Length) < nlmsgHeaderLen || int(m.Header.Length) > len(b) { + return errShortMessage + } + + m.Header.Type = HeaderType(binary.NativeEndian.Uint16(b[4:])) + m.Header.Flags = HeaderFlags(binary.NativeEndian.Uint16(b[6:])) + m.Header.Sequence = binary.NativeEndian.Uint32(b[8:]) + m.Header.PID = binary.NativeEndian.Uint32(b[12:]) + m.Data = b[nlmsgHeaderLen:m.Header.Length] + + return nil +} + +// checkMessage checks a single Message for netlink errors. +func checkMessage(m Message) error { + // NB: All non-nil errors returned from this function *must* be of type + // OpError in order to maintain the appropriate contract with callers of + // this package. + + // The libnl documentation indicates that type error can + // contain error codes: + // https://www.infradead.org/~tgr/libnl/doc/core.html#core_errmsg. + // + // However, rtnetlink at least seems to also allow errors to occur at the + // end of a multipart message with done/multi and an error number. + var hasHeader bool + switch { + case m.Header.Type == Error: + // Error code followed by nlmsghdr/ext ack attributes. + hasHeader = true + case m.Header.Type == Done && m.Header.Flags&Multi != 0: + // If no data, there must be no error number so just exit early. Some + // of the unit tests hard-coded this but I don't actually know if this + // case occurs in the wild. + if len(m.Data) == 0 { + return nil + } + + // Done|Multi potentially followed by ext ack attributes. + default: + // Neither, nothing to do. + return nil + } + + // Errno occupies 4 bytes. + const endErrno = 4 + if len(m.Data) < endErrno { + return newOpError("receive", errShortErrorMessage) + } + + c := int32(binary.NativeEndian.Uint32(m.Data[:endErrno])) + if c == 0 { + // 0 indicates no error. + return nil + } + + oerr := &OpError{ + Op: "receive", + // Error code is a negative integer, convert it into an OS-specific raw + // system call error, but do not wrap with os.NewSyscallError to signify + // that this error was produced by a netlink message; not a system call. + Err: newError(-1 * int(c)), + Sequence: m.Header.Sequence, + } + + // TODO(mdlayher): investigate the Capped flag. + + if m.Header.Flags&AcknowledgeTLVs == 0 { + // No extended acknowledgement. + return oerr + } + + // Flags indicate an extended acknowledgement. The type/flags combination + // checked above determines the offset where the TLVs occur. + var off int + if hasHeader { + // There is an nlmsghdr preceding the TLVs. + if len(m.Data) < endErrno+nlmsgHeaderLen { + return newOpError("receive", errShortErrorMessage) + } + + // The TLVs should be at the offset indicated by the nlmsghdr.length, + // plus the offset where the header began. But make sure the calculated + // offset is still in-bounds. + h := *(*Header)(unsafe.Pointer(&m.Data[endErrno : endErrno+nlmsgHeaderLen][0])) + off = endErrno + int(h.Length) + + if len(m.Data) < off { + return newOpError("receive", errShortErrorMessage) + } + } else { + // There is no nlmsghdr preceding the TLVs, parse them directly. + off = endErrno + } + + ad, err := NewAttributeDecoder(m.Data[off:]) + if err != nil { + // Malformed TLVs, just return the OpError with the info we have. + return oerr + } + + for ad.Next() { + switch ad.Type() { + case 1: // unix.NLMSGERR_ATTR_MSG + oerr.Message = ad.String() + case 2: // unix.NLMSGERR_ATTR_OFFS + oerr.Offset = int(ad.Uint32()) + } + } + + // Explicitly ignore ad.Err: malformed TLVs, just return the OpError with + // the info we have. + return oerr +} + +// parseMessagesIter returns an iterator over netlink messages in b. +// Each iteration yields a Message and any error encountered during parsing. +// The iterator stops on the first error or when all messages are parsed. +// +// If b is less that NLMSG_HDRLEN bytes, no error or messages will be returned. +// The same applies for any trailing bytes whose length is less than +// NLMSG_HDRLEN. +func parseMessagesIter(b []byte) iter.Seq2[Message, error] { + return func(yield func(Message, error) bool) { + for len(b) >= nlmsgHeaderLen { + length := binary.NativeEndian.Uint32(b[0:]) + if int(length) < nlmsgHeaderLen { + yield(Message{}, errIncorrectMessageLength) + return + } + + alength := nlmsgAlign(int(length)) + if alength > len(b) { + yield(Message{}, errShortMessage) + return + } + + var message Message + if err := message.UnmarshalBinary(b[:alength]); err != nil { + yield(Message{}, err) + return + } + + // Return if the consumer has stopped iterating. + if !yield(message, nil) { + return + } + + b = b[alength:] + } + } +} diff --git a/vendor/github.com/mdlayher/netlink/nlenc/doc.go b/vendor/github.com/mdlayher/netlink/nlenc/doc.go new file mode 100644 index 000000000..947d10f89 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/nlenc/doc.go @@ -0,0 +1,15 @@ +// Package nlenc implements encoding and decoding functions for netlink +// messages and attributes. +package nlenc + +import ( + "encoding/binary" +) + +// NativeEndian returns the native byte order of this system. +// +// Deprecated: Use [binary.NativeEndian] instead. +func NativeEndian() binary.ByteOrder { + // TODO(mdlayher): consider deprecating and removing this function for v2. + return binary.NativeEndian +} diff --git a/vendor/github.com/mdlayher/netlink/nlenc/int.go b/vendor/github.com/mdlayher/netlink/nlenc/int.go new file mode 100644 index 000000000..3bd99d741 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/nlenc/int.go @@ -0,0 +1,170 @@ +package nlenc + +import ( + "encoding/binary" + "fmt" +) + +// PutUint8 encodes a uint8 into b. +// If b is not exactly 1 byte in length, PutUint8 will panic. +// +// Deprecated: Use inline "b[n] = v" instead. +func PutUint8(b []byte, v uint8) { + if l := len(b); l != 1 { + panic(fmt.Sprintf("PutUint8: unexpected byte slice length: %d", l)) + } + + b[0] = v +} + +// PutUint16 encodes a uint16 into b using the host machine's native endianness. +// If b is not exactly 2 bytes in length, PutUint16 will panic. +// +// Deprecated: Use [binary.NativeEndian.PutUint16] instead. +func PutUint16(b []byte, v uint16) { + if l := len(b); l != 2 { + panic(fmt.Sprintf("PutUint16: unexpected byte slice length: %d", l)) + } + + binary.NativeEndian.PutUint16(b, v) +} + +// PutUint32 encodes a uint32 into b using the host machine's native endianness. +// If b is not exactly 4 bytes in length, PutUint32 will panic. +// +// Deprecated: Use [binary.NativeEndian.PutUint32] instead. +func PutUint32(b []byte, v uint32) { + if l := len(b); l != 4 { + panic(fmt.Sprintf("PutUint32: unexpected byte slice length: %d", l)) + } + + binary.NativeEndian.PutUint32(b, v) +} + +// PutUint64 encodes a uint64 into b using the host machine's native endianness. +// If b is not exactly 8 bytes in length, PutUint64 will panic. +// +// Deprecated: Use [binary.NativeEndian.PutUint64] instead. +func PutUint64(b []byte, v uint64) { + if l := len(b); l != 8 { + panic(fmt.Sprintf("PutUint64: unexpected byte slice length: %d", l)) + } + + binary.NativeEndian.PutUint64(b, v) +} + +// PutInt32 encodes a int32 into b using the host machine's native endianness. +// If b is not exactly 4 bytes in length, PutInt32 will panic. +// +// Deprecated: Use [binary.NativeEndian.PutUint32] instead (with a typecast). +func PutInt32(b []byte, v int32) { + if l := len(b); l != 4 { + panic(fmt.Sprintf("PutInt32: unexpected byte slice length: %d", l)) + } + + binary.NativeEndian.PutUint32(b, uint32(v)) +} + +// Uint8 decodes a uint8 from b. +// If b is not exactly 1 byte in length, Uint8 will panic. +// +// Deprecated: Use inline "b[n]" instead. +func Uint8(b []byte) uint8 { + if l := len(b); l != 1 { + panic(fmt.Sprintf("Uint8: unexpected byte slice length: %d", l)) + } + + return b[0] +} + +// Uint16 decodes a uint16 from b using the host machine's native endianness. +// If b is not exactly 2 bytes in length, Uint16 will panic. +// +// Deprecated: Use [binary.NativeEndian.Uint16] instead. +func Uint16(b []byte) uint16 { + if l := len(b); l != 2 { + panic(fmt.Sprintf("Uint16: unexpected byte slice length: %d", l)) + } + + return binary.NativeEndian.Uint16(b) +} + +// Uint32 decodes a uint32 from b using the host machine's native endianness. +// If b is not exactly 4 bytes in length, Uint32 will panic. +// +// Deprecated: Use [binary.NativeEndian.Uint32] instead. +func Uint32(b []byte) uint32 { + if l := len(b); l != 4 { + panic(fmt.Sprintf("Uint32: unexpected byte slice length: %d", l)) + } + + return binary.NativeEndian.Uint32(b) +} + +// Uint64 decodes a uint64 from b using the host machine's native endianness. +// If b is not exactly 8 bytes in length, Uint64 will panic. +// +// Deprecated: Use [binary.NativeEndian.Uint64] instead. +func Uint64(b []byte) uint64 { + if l := len(b); l != 8 { + panic(fmt.Sprintf("Uint64: unexpected byte slice length: %d", l)) + } + + return binary.NativeEndian.Uint64(b) +} + +// Int32 decodes an int32 from b using the host machine's native endianness. +// If b is not exactly 4 bytes in length, Int32 will panic. +// +// Deprecated: Use [binary.NativeEndian.Uint32] instead (with a typecast). +func Int32(b []byte) int32 { + if l := len(b); l != 4 { + panic(fmt.Sprintf("Int32: unexpected byte slice length: %d", l)) + } + + return int32(binary.NativeEndian.Uint32(b)) +} + +// Uint8Bytes encodes a uint8 into a newly-allocated byte slice. It is a +// shortcut for allocating a new byte slice and filling it using PutUint8. +// +// Deprecated: Use inline "[]byte{v}" instead. +func Uint8Bytes(v uint8) []byte { + return []byte{v} +} + +// Uint16Bytes encodes a uint16 into a newly-allocated byte slice using the +// host machine's native endianness. It is a shortcut for allocating a new +// byte slice and filling it using PutUint16. +func Uint16Bytes(v uint16) []byte { + b := make([]byte, 2) + binary.NativeEndian.PutUint16(b, v) + return b +} + +// Uint32Bytes encodes a uint32 into a newly-allocated byte slice using the +// host machine's native endianness. It is a shortcut for allocating a new +// byte slice and filling it using PutUint32. +func Uint32Bytes(v uint32) []byte { + b := make([]byte, 4) + binary.NativeEndian.PutUint32(b, v) + return b +} + +// Uint64Bytes encodes a uint64 into a newly-allocated byte slice using the +// host machine's native endianness. It is a shortcut for allocating a new +// byte slice and filling it using PutUint64. +func Uint64Bytes(v uint64) []byte { + b := make([]byte, 8) + binary.NativeEndian.PutUint64(b, v) + return b +} + +// Int32Bytes encodes a int32 into a newly-allocated byte slice using the +// host machine's native endianness. It is a shortcut for allocating a new +// byte slice and filling it using PutInt32. +func Int32Bytes(v int32) []byte { + b := make([]byte, 4) + binary.NativeEndian.PutUint32(b, uint32(v)) + return b +} diff --git a/vendor/github.com/mdlayher/netlink/nlenc/string.go b/vendor/github.com/mdlayher/netlink/nlenc/string.go new file mode 100644 index 000000000..c0b166ddf --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/nlenc/string.go @@ -0,0 +1,18 @@ +package nlenc + +import "bytes" + +// Bytes returns a null-terminated byte slice with the contents of s. +func Bytes(s string) []byte { + return append([]byte(s), 0x00) +} + +// String returns a string with the contents of b from a null-terminated +// byte slice. +func String(b []byte) string { + // If the string has more than one NULL terminator byte, we want to remove + // all of them before returning the string to the caller; hence the use of + // strings.TrimRight instead of strings.TrimSuffix (which previously only + // removed a single NULL). + return string(bytes.TrimRight(b, "\x00")) +} diff --git a/vendor/github.com/mdlayher/netlink/nltest/errors_others.go b/vendor/github.com/mdlayher/netlink/nltest/errors_others.go new file mode 100644 index 000000000..3a29c9b1a --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/nltest/errors_others.go @@ -0,0 +1,8 @@ +//go:build plan9 || windows +// +build plan9 windows + +package nltest + +func isSyscallError(_ error) bool { + return false +} diff --git a/vendor/github.com/mdlayher/netlink/nltest/errors_unix.go b/vendor/github.com/mdlayher/netlink/nltest/errors_unix.go new file mode 100644 index 000000000..8b638da75 --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/nltest/errors_unix.go @@ -0,0 +1,15 @@ +//go:build !plan9 && !windows + +package nltest + +import ( + "errors" + + "golang.org/x/sys/unix" +) + +func isSyscallError(err error) bool { + var errno unix.Errno + ok := errors.As(err, &errno) + return ok +} diff --git a/vendor/github.com/mdlayher/netlink/nltest/nltest.go b/vendor/github.com/mdlayher/netlink/nltest/nltest.go new file mode 100644 index 000000000..143e4ca2f --- /dev/null +++ b/vendor/github.com/mdlayher/netlink/nltest/nltest.go @@ -0,0 +1,249 @@ +// Package nltest provides utilities for netlink testing. +package nltest + +import ( + "errors" + "fmt" + "io" + "iter" + "os" + + "github.com/mdlayher/netlink" + "github.com/mdlayher/netlink/nlenc" +) + +// PID is the netlink header PID value assigned by nltest. +const PID = 1 + +// MustMarshalAttributes marshals a slice of netlink.Attributes to their binary +// format, but panics if any errors occur. +func MustMarshalAttributes(attrs []netlink.Attribute) []byte { + b, err := netlink.MarshalAttributes(attrs) + if err != nil { + panic(fmt.Sprintf("failed to marshal attributes to binary: %v", err)) + } + + return b +} + +// Multipart sends a slice of netlink.Messages to the caller as a +// netlink multi-part message. If less than two messages are present, +// the messages are not altered. +func Multipart(msgs []netlink.Message) ([]netlink.Message, error) { + if len(msgs) < 2 { + return msgs, nil + } + + for i := range msgs { + // Last message has header type "done" in addition to multi-part flag. + if i == len(msgs)-1 { + msgs[i].Header.Type = netlink.Done + } + + msgs[i].Header.Flags |= netlink.Multi + } + + return msgs, nil +} + +// Error returns a netlink error to the caller with the specified error +// number, in the body of the specified request message. +func Error(number int, reqs []netlink.Message) ([]netlink.Message, error) { + req := reqs[0] + req.Header.Length += 4 + req.Header.Type = netlink.Error + + errno := -1 * int32(number) + req.Data = append(nlenc.Int32Bytes(errno), req.Data...) + + return []netlink.Message{req}, nil +} + +// A Func is a function that can be used to test netlink.Conn interactions. +// The function can choose to return zero or more netlink messages, or an +// error if needed. +// +// For a netlink request/response interaction, a request req is populated by +// netlink.Conn.Send and passed to the function. +// +// For multicast interactions, an empty request req is passed to the function +// when netlink.Conn.Receive is called. +// +// If a Func returns an error, the error will be returned as-is to the caller. +// If no messages and io.EOF are returned, no messages and no error will be +// returned to the caller, simulating a multi-part message with no data. +type Func func(req []netlink.Message) ([]netlink.Message, error) + +// Dial sets up a netlink.Conn for testing using the specified Func. All requests +// sent from the connection will be passed to the Func. The connection should be +// closed as usual when it is no longer needed. +func Dial(fn Func) *netlink.Conn { + sock := &socket{ + fn: fn, + } + + return netlink.NewConn(sock, PID) +} + +// CheckRequest returns a Func that verifies that each message in an incoming +// request has the specified netlink header type and flags in the same slice +// position index, and then passes the request through to fn. +// +// The length of the types and flags slices must match the number of requests +// passed to the returned Func, or CheckRequest will panic. +// +// As an example: +// - types[0] and flags[0] will be checked against reqs[0] +// - types[1] and flags[1] will be checked against reqs[1] +// - ... and so on +// +// If an element of types or flags is set to the zero value, that check will +// be skipped for the request message that occurs at the same index. +// +// As an example, if types[0] is 0 and reqs[0].Header.Type is 1, the check will +// succeed because types[0] was not specified. +func CheckRequest(types []netlink.HeaderType, flags []netlink.HeaderFlags, fn Func) Func { + if len(types) != len(flags) { + panicf("nltest: CheckRequest called with mismatched types and flags slice lengths: %d != %d", + len(types), len(flags)) + } + + return func(req []netlink.Message) ([]netlink.Message, error) { + if len(types) != len(req) { + panicf("nltest: CheckRequest function invoked types/flags and request message slice lengths: %d != %d", + len(types), len(req)) + } + + for i := range req { + if want, got := types[i], req[i].Header.Type; types[i] != 0 && want != got { + return nil, fmt.Errorf("nltest: unexpected netlink header type: %s, want: %s", got, want) + } + + if want, got := flags[i], req[i].Header.Flags; flags[i] != 0 && want != got { + return nil, fmt.Errorf("nltest: unexpected netlink header flags: %s, want: %s", got, want) + } + } + + return fn(req) + } +} + +// A socket is a netlink.Socket used for testing. +type socket struct { + fn Func + + msgs []netlink.Message + err error +} + +func (c *socket) Close() error { return nil } + +func (c *socket) SendMessages(messages []netlink.Message) error { + msgs, err := c.fn(messages) + c.msgs = append(c.msgs, msgs...) + c.err = err + return nil +} + +func (c *socket) Send(m netlink.Message) error { + c.msgs, c.err = c.fn([]netlink.Message{m}) + return nil +} + +func (c *socket) Receive() ([]netlink.Message, error) { + var msgs []netlink.Message + for msg, err := range c.ReceiveIter() { + if err != nil { + return nil, err + } + msgs = append(msgs, msg) + } + + return msgs, nil +} + +func (c *socket) ReceiveIter() iter.Seq2[netlink.Message, error] { + return func(yield func(netlink.Message, error) bool) { + // No messages set by Send means that we are emulating a + // multicast response or an error occurred. + if len(c.msgs) == 0 { + switch { + case c.err == nil: + msgs, err := c.fn(nil) + if errors.Is(err, io.EOF) { + return + } + if err != nil { + yield(netlink.Message{}, err) + return + } + for _, m := range msgs { + if !yield(m, nil) { + return + } + } + return + case errors.Is(c.err, io.EOF): + return + } + + // If the error is a system call error, wrap it in os.NewSyscallError + // to simulate what the Linux netlink.Conn does. + if isSyscallError(c.err) { + err := c.err + c.err = nil + yield(netlink.Message{}, os.NewSyscallError("recvmsg", err)) + return + } + + // Some generic error occurred and should be passed to the caller. + err := c.err + c.err = nil + yield(netlink.Message{}, err) + return + } + + // Detect multi-part messages. + var multi bool + for _, m := range c.msgs { + if m.Header.Flags&netlink.Multi != 0 && m.Header.Type != netlink.Done { + multi = true + } + } + + // When a multi-part message is detected, the messages are returned in + // batches of half the total messages, so that multiple calls to Receive or + // ReceiveIter from netlink.Conn are needed to drain all messages. + if multi { + batchSize := (len(c.msgs) + 1) / 2 + batch := c.msgs[:batchSize] + c.msgs = c.msgs[batchSize:] + + for _, m := range batch { + if !yield(m, nil) { + return + } + } + + return + } + + msgs, err := c.msgs, c.err + c.msgs, c.err = nil, nil + + if err != nil { + yield(netlink.Message{}, err) + return + } + + for _, m := range msgs { + if !yield(m, nil) { + return + } + } + } +} + +func panicf(format string, a ...any) { + panic(fmt.Sprintf(format, a...)) +} diff --git a/vendor/github.com/mdlayher/socket/.golangci.yml b/vendor/github.com/mdlayher/socket/.golangci.yml new file mode 100644 index 000000000..1f10166aa --- /dev/null +++ b/vendor/github.com/mdlayher/socket/.golangci.yml @@ -0,0 +1,16 @@ +version: "2" +linters: + enable: + - misspell + - modernize + - revive + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling +formatters: + exclusions: + generated: lax diff --git a/vendor/github.com/mdlayher/socket/CHANGELOG.md b/vendor/github.com/mdlayher/socket/CHANGELOG.md index b83418532..b94818e7c 100644 --- a/vendor/github.com/mdlayher/socket/CHANGELOG.md +++ b/vendor/github.com/mdlayher/socket/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## v0.5.2 + +- [Improvement]: Bump build to Go 1.23.0. Note this is required for the latest + Go extended library versions. + ## v0.5.1 - [Improvement]: revert `go.mod` to Go 1.20 to [resolve an issue around Go diff --git a/vendor/github.com/mdlayher/socket/accept4.go b/vendor/github.com/mdlayher/socket/accept4.go index e1016b206..48be812ed 100644 --- a/vendor/github.com/mdlayher/socket/accept4.go +++ b/vendor/github.com/mdlayher/socket/accept4.go @@ -1,5 +1,4 @@ //go:build dragonfly || freebsd || illumos || linux -// +build dragonfly freebsd illumos linux package socket diff --git a/vendor/github.com/mdlayher/socket/conn.go b/vendor/github.com/mdlayher/socket/conn.go index 5be502f5a..6057a2e0a 100644 --- a/vendor/github.com/mdlayher/socket/conn.go +++ b/vendor/github.com/mdlayher/socket/conn.go @@ -120,7 +120,7 @@ func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) { b = b[:maxRW] } - n, err := readT(c, ctx, "read", func(fd int) (int, error) { + n, err := readT(ctx, c, "read", func(fd int) (int, error) { return unix.Read(fd, b) }) if n == 0 && err == nil && c.facts.zeroReadIsEOF { @@ -142,12 +142,12 @@ func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) { ) doErr := c.write(ctx, "write", func(fd int) error { - max := len(b) - if c.facts.isStream && max-nn > maxRW { - max = nn + maxRW + lenb := len(b) + if c.facts.isStream && lenb-nn > maxRW { + lenb = nn + maxRW } - n, err = unix.Write(fd, b[nn:max]) + n, err = unix.Write(fd, b[nn:lenb]) if n > 0 { nn += n } @@ -418,7 +418,7 @@ func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, err sa unix.Sockaddr } - r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) { + r, err := readT(ctx, c, sysAccept, func(fd int) (ret, error) { // Either accept(2) or accept4(2) depending on the OS. nfd, sa, err := accept(fd, flags|socketFlags) return ret{nfd, sa}, err @@ -464,7 +464,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er // have an explicit WaitWrite call like internal/poll does, so we have // to wait until the runtime calls the closure again to indicate we can // write. - progress uint32 + progress atomic.Uint32 // Capture closure sockaddr and error. rsa unix.Sockaddr @@ -472,7 +472,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er ) doErr := c.write(ctx, op, func(fd int) error { - if atomic.AddUint32(&progress, 1) == 1 { + if progress.Add(1) == 1 { // First call: initiate connect. return unix.Connect(fd, sa) } @@ -569,7 +569,7 @@ func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int, from unix.Sockaddr } - r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) { + r, err := readT(ctx, c, "recvmsg", func(fd int) (ret, error) { n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags) return ret{n, oobn, recvflags, from}, err }) @@ -587,7 +587,7 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc addr unix.Sockaddr } - out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) { + out, err := readT(ctx, c, "recvfrom", func(fd int) (ret, error) { n, addr, err := unix.Recvfrom(fd, p, flags) return ret{n, addr}, err }) @@ -600,7 +600,7 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc // Sendmsg wraps sendmsg(2). func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) { - return writeT(c, ctx, "sendmsg", func(fd int) (int, error) { + return writeT(ctx, c, "sendmsg", func(fd int) (int, error) { return unix.SendmsgN(fd, p, oob, to, flags) }) } @@ -645,7 +645,7 @@ func (c *Conn) Shutdown(how int) error { // read wraps readT to execute a function and capture its error result. This is // a convenience wrapper for functions which don't return any extra values. func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error { - _, err := readT(c, ctx, op, func(fd int) (struct{}, error) { + _, err := readT(ctx, c, op, func(fd int) (struct{}, error) { return struct{}{}, f(fd) }) return err @@ -654,7 +654,7 @@ func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error // write executes f, a write function, against the associated file descriptor. // op is used to create an *os.SyscallError if the file descriptor is closed. func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error { - _, err := writeT(c, ctx, op, func(fd int) (struct{}, error) { + _, err := writeT(ctx, c, op, func(fd int) (struct{}, error) { return struct{}{}, f(fd) }) return err @@ -662,7 +662,7 @@ func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error // readT executes c.rc.Read for op using the input function, returning a newly // allocated result T. -func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) { +func readT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) { return rwT(c, rwContext[T]{ Context: ctx, Type: read, @@ -673,7 +673,7 @@ func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, er // writeT executes c.rc.Write for op using the input function, returning a newly // allocated result T. -func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) { +func writeT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) { return rwT(c, rwContext[T]{ Context: ctx, Type: write, @@ -771,9 +771,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) { // // TODO(mdlayher): is it possible to detect a background context vs a // context with possible future cancel? - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { select { case <-rw.Context.Done(): @@ -784,7 +782,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) { case <-doneC: // Nothing to do. } - }() + }) } var ( diff --git a/vendor/github.com/mdlayher/socket/conn_linux.go b/vendor/github.com/mdlayher/socket/conn_linux.go index 081194f32..b1cdffbdc 100644 --- a/vendor/github.com/mdlayher/socket/conn_linux.go +++ b/vendor/github.com/mdlayher/socket/conn_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package socket diff --git a/vendor/github.com/mdlayher/socket/netns_linux.go b/vendor/github.com/mdlayher/socket/netns_linux.go index b29115ad1..9f37b7702 100644 --- a/vendor/github.com/mdlayher/socket/netns_linux.go +++ b/vendor/github.com/mdlayher/socket/netns_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package socket @@ -65,7 +64,7 @@ func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) { // No more thread-local state manipulation; return the new Conn. runtime.UnlockOSThread() conn = c - return nil + return err }) if err := eg.Wait(); err != nil { diff --git a/vendor/github.com/mdlayher/socket/setbuffer_linux.go b/vendor/github.com/mdlayher/socket/setbuffer_linux.go index 0d4aa4417..ae631893a 100644 --- a/vendor/github.com/mdlayher/socket/setbuffer_linux.go +++ b/vendor/github.com/mdlayher/socket/setbuffer_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package socket diff --git a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go b/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go index 40e834310..f4a7e559b 100644 --- a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go +++ b/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go @@ -1,5 +1,4 @@ //go:build !darwin -// +build !darwin package socket diff --git a/vendor/github.com/onsi/gomega/CHANGELOG.md b/vendor/github.com/onsi/gomega/CHANGELOG.md index 9c94d0e6c..7cd26c2ce 100644 --- a/vendor/github.com/onsi/gomega/CHANGELOG.md +++ b/vendor/github.com/onsi/gomega/CHANGELOG.md @@ -1,3 +1,21 @@ +## 1.42.1 + +Bump Dependencies + +## 1.42.0 + +Add a set of Claude skill as a marketplace plugin + +## 1.41.0 + +### Features + +Add `BeASlice` and `BeAnArray` matchers + +### Fixes + +Object formatting now detects pointer cycles to avoid runaway formatting output. + ## 1.40.0 We're adopting a new release strategy to minimize dependency bloat in projects that consume Gomega. It is a limitation of the go mod toolchain that _test_ subdependencies of your project's direct dependencies get pulled in as *indirect* dependencies. In the case of Gomega, this ends up pulling in all of Ginkgo into your `go.mod` even if you are only using Gomega (Gomega uses Ginkgo for its own tests). diff --git a/vendor/github.com/onsi/gomega/README.md b/vendor/github.com/onsi/gomega/README.md index d45a8c4e5..6eb36fdf8 100644 --- a/vendor/github.com/onsi/gomega/README.md +++ b/vendor/github.com/onsi/gomega/README.md @@ -6,6 +6,19 @@ Jump straight to the [docs](http://onsi.github.io/gomega/) to learn about Gomega If you have a question, comment, bug report, feature request, etc. please open a GitHub issue. +## Using Gomega with Claude Code + +Gomega ships a set of [Claude Code](https://claude.com/claude-code) skills as a **plugin**, so an agent writing assertions in *your* suite has Gomega's idioms — the full matcher catalog, `Eventually`/`Consistently`, and the `gstruct`/`ghttp`/`gexec`/`gbytes`/`gleak`/`gmeasure` sub-libraries — on hand. The Gomega repo doubles as the plugin marketplace, so installation is two commands. From inside Claude Code: + +``` +/plugin marketplace add onsi/gomega +/plugin install gomega@gomega +``` + +(or non-interactively: `claude plugin marketplace add onsi/gomega` then `claude plugin install gomega@gomega`) + +This installs a family of `gomega:*` skills that activate automatically while you write tests. See the [docs](http://onsi.github.io/gomega/#using-gomega-with-claude-code) for the full list. + ## [Ginkgo](http://github.com/onsi/ginkgo): a BDD Testing Framework for Golang Learn more about Ginkgo [here](http://onsi.github.io/ginkgo/) diff --git a/vendor/github.com/onsi/gomega/format/format.go b/vendor/github.com/onsi/gomega/format/format.go index 6c23ba338..d56f9a475 100644 --- a/vendor/github.com/onsi/gomega/format/format.go +++ b/vendor/github.com/onsi/gomega/format/format.go @@ -262,7 +262,7 @@ func Object(object any, indentation uint) string { if err, ok := object.(error); ok && !isNilValue(value) { // isNilValue check needed here to avoid nil deref due to boxed nil commonRepresentation += "\n" + IndentString(err.Error(), indentation) + "\n" + indent } - return fmt.Sprintf("%s<%s>: %s%s", indent, formatType(value), commonRepresentation, formatValue(value, indentation, true)) + return fmt.Sprintf("%s<%s>: %s%s", indent, formatType(value), commonRepresentation, formatValue(value, indentation, true, map[uintptr]struct{}{})) } /* @@ -306,7 +306,7 @@ func formatType(v reflect.Value) string { } } -func formatValue(value reflect.Value, indentation uint, isTopLevel bool) string { +func formatValue(value reflect.Value, indentation uint, isTopLevel bool, visited map[uintptr]struct{}) string { if indentation > MaxDepth { return "..." } @@ -367,23 +367,28 @@ func formatValue(value reflect.Value, indentation uint, isTopLevel bool) string case reflect.Func: return fmt.Sprintf("0x%x", value.Pointer()) case reflect.Ptr: - return formatValue(value.Elem(), indentation, isTopLevel) + ptr := value.Pointer() + if _, ok := visited[ptr]; ok { + return fmt.Sprintf("0x%x (cyclic reference)", ptr) + } + visited[ptr] = struct{}{} + return formatValue(value.Elem(), indentation, isTopLevel, visited) case reflect.Slice: - return truncateLongStrings(formatSlice(value, indentation)) + return truncateLongStrings(formatSlice(value, indentation, visited)) case reflect.String: return truncateLongStrings(formatString(value.String(), indentation, isTopLevel)) case reflect.Array: - return truncateLongStrings(formatSlice(value, indentation)) + return truncateLongStrings(formatSlice(value, indentation, visited)) case reflect.Map: - return truncateLongStrings(formatMap(value, indentation)) + return truncateLongStrings(formatMap(value, indentation, visited)) case reflect.Struct: if value.Type() == timeType && value.CanInterface() { t, _ := value.Interface().(time.Time) return t.Format(time.RFC3339Nano) } - return truncateLongStrings(formatStruct(value, indentation)) + return truncateLongStrings(formatStruct(value, indentation, visited)) case reflect.Interface: - return formatInterface(value, indentation) + return formatInterface(value, indentation, visited) default: if value.CanInterface() { return truncateLongStrings(fmt.Sprintf("%#v", value.Interface())) @@ -414,7 +419,7 @@ func formatString(object any, indentation uint, isTopLevel bool) string { } } -func formatSlice(v reflect.Value, indentation uint) string { +func formatSlice(v reflect.Value, indentation uint, visited map[uintptr]struct{}) string { if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && isPrintableString(string(v.Bytes())) { return formatString(v.Bytes(), indentation, false) } @@ -423,7 +428,7 @@ func formatSlice(v reflect.Value, indentation uint) string { result := make([]string, l) longest := 0 for i := range l { - result[i] = formatValue(v.Index(i), indentation+1, false) + result[i] = formatValue(v.Index(i), indentation+1, false, visited) if len(result[i]) > longest { longest = len(result[i]) } @@ -436,14 +441,14 @@ func formatSlice(v reflect.Value, indentation uint) string { return fmt.Sprintf("[%s]", strings.Join(result, ", ")) } -func formatMap(v reflect.Value, indentation uint) string { +func formatMap(v reflect.Value, indentation uint, visited map[uintptr]struct{}) string { l := v.Len() result := make([]string, l) longest := 0 for i, key := range v.MapKeys() { value := v.MapIndex(key) - result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1, false), formatValue(value, indentation+1, false)) + result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1, false, visited), formatValue(value, indentation+1, false, visited)) if len(result[i]) > longest { longest = len(result[i]) } @@ -456,7 +461,7 @@ func formatMap(v reflect.Value, indentation uint) string { return fmt.Sprintf("{%s}", strings.Join(result, ", ")) } -func formatStruct(v reflect.Value, indentation uint) string { +func formatStruct(v reflect.Value, indentation uint, visited map[uintptr]struct{}) string { t := v.Type() l := v.NumField() @@ -465,7 +470,7 @@ func formatStruct(v reflect.Value, indentation uint) string { for i := range l { structField := t.Field(i) fieldEntry := v.Field(i) - representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1, false)) + representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1, false, visited)) result = append(result, representation) if len(representation) > longest { longest = len(representation) @@ -478,8 +483,8 @@ func formatStruct(v reflect.Value, indentation uint) string { return fmt.Sprintf("{%s}", strings.Join(result, ", ")) } -func formatInterface(v reflect.Value, indentation uint) string { - return fmt.Sprintf("<%s>%s", formatType(v.Elem()), formatValue(v.Elem(), indentation, false)) +func formatInterface(v reflect.Value, indentation uint, visited map[uintptr]struct{}) string { + return fmt.Sprintf("<%s>%s", formatType(v.Elem()), formatValue(v.Elem(), indentation, false, visited)) } func isNilValue(a reflect.Value) bool { diff --git a/vendor/github.com/onsi/gomega/gomega_dsl.go b/vendor/github.com/onsi/gomega/gomega_dsl.go index af1341bdb..db84085be 100644 --- a/vendor/github.com/onsi/gomega/gomega_dsl.go +++ b/vendor/github.com/onsi/gomega/gomega_dsl.go @@ -22,7 +22,7 @@ import ( "github.com/onsi/gomega/types" ) -const GOMEGA_VERSION = "1.40.0" +const GOMEGA_VERSION = "1.42.1" const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler. If you're using Ginkgo then you probably forgot to put your assertion in an It(). diff --git a/vendor/github.com/onsi/gomega/matchers.go b/vendor/github.com/onsi/gomega/matchers.go index 16ca8f46d..bf5722605 100644 --- a/vendor/github.com/onsi/gomega/matchers.go +++ b/vendor/github.com/onsi/gomega/matchers.go @@ -621,6 +621,18 @@ func BeADirectory() types.GomegaMatcher { return &matchers.BeADirectoryMatcher{} } +// BeASlice succeeds if actual is a value of slice type. +// This is useful when actual has type any (interface{}) and you want to assert it is a slice. +func BeASlice() types.GomegaMatcher { + return &matchers.BeASliceMatcher{} +} + +// BeAnArray succeeds if actual is a value of array type. +// This is useful when actual has type any (interface{}) and you want to assert it is an array. +func BeAnArray() types.GomegaMatcher { + return &matchers.BeAnArrayMatcher{} +} + // HaveHTTPStatus succeeds if the Status or StatusCode field of an HTTP response matches. // Actual must be either a *http.Response or *httptest.ResponseRecorder. // Expected must be either an int or a string. diff --git a/vendor/github.com/onsi/gomega/matchers/be_a_slice_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_a_slice_matcher.go new file mode 100644 index 000000000..4fcad5127 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_a_slice_matcher.go @@ -0,0 +1,28 @@ +// untested sections: 1 + +package matchers + +import ( + "fmt" + "reflect" + + "github.com/onsi/gomega/format" +) + +type BeASliceMatcher struct { +} + +func (matcher *BeASliceMatcher) Match(actual any) (success bool, err error) { + if actual == nil { + return false, fmt.Errorf("BeASlice matcher expects a value, got nil") + } + return reflect.TypeOf(actual).Kind() == reflect.Slice, nil +} + +func (matcher *BeASliceMatcher) FailureMessage(actual any) (message string) { + return format.Message(actual, "to be a slice") +} + +func (matcher *BeASliceMatcher) NegatedFailureMessage(actual any) (message string) { + return format.Message(actual, "not to be a slice") +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_an_array_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_an_array_matcher.go new file mode 100644 index 000000000..573aa8198 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_an_array_matcher.go @@ -0,0 +1,28 @@ +// untested sections: 1 + +package matchers + +import ( + "fmt" + "reflect" + + "github.com/onsi/gomega/format" +) + +type BeAnArrayMatcher struct { +} + +func (matcher *BeAnArrayMatcher) Match(actual any) (success bool, err error) { + if actual == nil { + return false, fmt.Errorf("BeAnArray matcher expects a value, got nil") + } + return reflect.TypeOf(actual).Kind() == reflect.Array, nil +} + +func (matcher *BeAnArrayMatcher) FailureMessage(actual any) (message string) { + return format.Message(actual, "to be an array") +} + +func (matcher *BeAnArrayMatcher) NegatedFailureMessage(actual any) (message string) { + return format.Message(actual, "not to be an array") +} diff --git a/vendor/github.com/onsi/gomega/types/types.go b/vendor/github.com/onsi/gomega/types/types.go index 685a46f37..e444451ac 100644 --- a/vendor/github.com/onsi/gomega/types/types.go +++ b/vendor/github.com/onsi/gomega/types/types.go @@ -66,6 +66,12 @@ func MatchMayChangeInTheFuture(matcher GomegaMatcher, value any) bool { // AsyncAssertions are returned by Eventually and Consistently and enable matchers to be polled repeatedly to ensure // they are eventually satisfied +// +// The optional optionalDescription argument allows you to annotate the assertion with additional information. +// It is passed as the second argument and can be a format string followed by arguments, or a func() string. +// The description is included in failure messages to provide context. +// +// For details on annotating assertions, see: https://onsi.github.io/gomega/#annotating-assertions type AsyncAssertion interface { Should(matcher GomegaMatcher, optionalDescription ...any) bool ShouldNot(matcher GomegaMatcher, optionalDescription ...any) bool @@ -86,6 +92,12 @@ type AsyncAssertion interface { } // Assertions are returned by Ω and Expect and enable assertions against Gomega matchers +// +// The optional optionalDescription argument allows you to annotate the assertion with additional information. +// It is passed as the second argument and can be a format string followed by arguments, or a func() string. +// The description is included in failure messages to provide context. +// +// For details on annotating assertions, see: https://onsi.github.io/gomega/#annotating-assertions type Assertion interface { Should(matcher GomegaMatcher, optionalDescription ...any) bool ShouldNot(matcher GomegaMatcher, optionalDescription ...any) bool diff --git a/vendor/golang.org/x/net/bpf/doc.go b/vendor/golang.org/x/net/bpf/doc.go index 04ec1c8ab..1ea566b7f 100644 --- a/vendor/golang.org/x/net/bpf/doc.go +++ b/vendor/golang.org/x/net/bpf/doc.go @@ -49,6 +49,13 @@ to extensions, which are essentially calls to kernel utility functions. Currently, the only extensions supported by this package are the Linux packet filter extensions. +# Security Considerations + +The implementation of the BPF VM in this package is suitable for +testing BPF programs. It aims for consistency with other BPF VM +implementations, but divergence in behavior is not considered a +security issue. + # Examples This packet filter selects all ARP packets. diff --git a/vendor/golang.org/x/net/html/entity.go b/vendor/golang.org/x/net/html/entity.go index b628880a0..4e8d5d55f 100644 --- a/vendor/golang.org/x/net/html/entity.go +++ b/vendor/golang.org/x/net/html/entity.go @@ -2156,9 +2156,8 @@ var entity = map[string]rune{ // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ - // TODO(nigeltao): Handle replacements that are wider than their names. - // "nLt;": {'\u226A', '\u20D2'}, - // "nGt;": {'\u226B', '\u20D2'}, + "nLt;": {'\u226A', '\u20D2'}, + "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, diff --git a/vendor/golang.org/x/net/html/escape.go b/vendor/golang.org/x/net/html/escape.go index 12f227370..df3edc5b1 100644 --- a/vendor/golang.org/x/net/html/escape.go +++ b/vendor/golang.org/x/net/html/escape.go @@ -6,6 +6,7 @@ package html import ( "bytes" + "slices" "strings" "unicode/utf8" ) @@ -50,25 +51,24 @@ var replacementTable = [...]rune{ // 0x0D->'\u000D' is a no-op. } -// unescapeEntity reads an entity like "<" from b[src:] and writes the -// corresponding "<" to b[dst:], returning the incremented dst and src cursors. -// Precondition: b[src] == '&' && dst <= src. -// attribute should be true if parsing an attribute value. -func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { +// unescapeEntity attempts to consume a character reference from s[src:], +// returning the rune, potential second rune, and number of bytes consumed +// (which indicates the length of the character reference). It is assumed that +// the first byte of s is '&'. attribute should be true if parsing an attribute +// value. +func unescapeEntity(s []byte, attribute bool) (rune, rune, int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. - i, s := 1, b[src:] + i := 1 if len(s) <= 1 { - b[dst] = b[src] - return dst + 1, src + 1 + return '&', 0, 1 } if s[i] == '#' { - if len(s) <= 3 { // We need to have at least "&#.". - b[dst] = b[src] - return dst + 1, src + 1 + if len(s) <= 2 { // We need to have at least "&#". + return '&', 0, 1 } i++ c := s[i] @@ -78,34 +78,43 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { i++ } + i0 := i x := '\x00' for i < len(s) { c = s[i] - i++ + var d rune + var mult rune if hex { + mult = 16 if '0' <= c && c <= '9' { - x = 16*x + rune(c) - '0' - continue + d = rune(c) - '0' } else if 'a' <= c && c <= 'f' { - x = 16*x + rune(c) - 'a' + 10 - continue + d = rune(c) - 'a' + 10 } else if 'A' <= c && c <= 'F' { - x = 16*x + rune(c) - 'A' + 10 - continue + d = rune(c) - 'A' + 10 + } else { + break + } + } else { + mult = 10 + if '0' <= c && c <= '9' { + d = rune(c) - '0' + } else { + break } - } else if '0' <= c && c <= '9' { - x = 10*x + rune(c) - '0' - continue } - if c != ';' { - i-- + if x <= 0x10FFFF { + x = mult*x + d } - break + i++ + } + + if i == i0 { // No characters matched. + return '&', 0, 1 } - if i <= 3 { // No characters matched. - b[dst] = b[src] - return dst + 1, src + 1 + if i < len(s) && s[i] == ';' { + i++ } if 0x80 <= x && x <= 0x9F { @@ -116,7 +125,7 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { x = '\uFFFD' } - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } // Consume the maximum number of characters possible, with the @@ -141,10 +150,9 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } else if x := entity2[entityName]; x[0] != 0 { - dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) - return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + return x[0], x[1], i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { @@ -152,35 +160,67 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 + return x, 0, j + 1 } } } - dst1, src1 = dst+i, src+i - copy(b[dst:dst1], b[src:src1]) - return dst1, src1 + return '&', 0, 1 } -// unescape unescapes b's entities in-place, so that "a<b" becomes "a entityNameLen { + if reusingB { + out = slices.Clone(out) + reusingB = false } - return b[0:dst] + out = slices.Grow(out, replLen) + } + out = utf8.AppendRune(out, r1) + if r2 != 0 { + out = utf8.AppendRune(out, r2) } + + src += entityNameLen } - return b + + return out } // lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". diff --git a/vendor/golang.org/x/net/html/foreign.go b/vendor/golang.org/x/net/html/foreign.go index e8515d8e8..65d01d1ed 100644 --- a/vendor/golang.org/x/net/html/foreign.go +++ b/vendor/golang.org/x/net/html/foreign.go @@ -23,7 +23,7 @@ func adjustForeignAttributes(aa []Attribute) { } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", - "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": + "xlink:title", "xlink:type", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index b3d2a2558..165b6108d 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -63,7 +63,7 @@ func (p *parser) top() *Node { // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ - "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template, a.Select}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } @@ -78,7 +78,6 @@ const ( tableScope tableRowScope tableBodyScope - selectScope ) // popUntil pops the stack of open elements at the highest element whose tag @@ -133,10 +132,6 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } - case selectScope: - if tagAtom != a.Optgroup && tagAtom != a.Option { - return -1 - } default: panic(fmt.Sprintf("html: internal error: indexOfElementInScope unknown scope: %d", s)) } @@ -460,21 +455,6 @@ func (p *parser) resetInsertionMode() { } switch n.DataAtom { - case a.Select: - if !last { - for ancestor, first := n, p.oe[0]; ancestor != first; { - ancestor = p.oe[p.oe.index(ancestor)-1] - switch ancestor.DataAtom { - case a.Template: - p.im = inSelectIM - return - case a.Table: - p.im = inSelectInTableIM - return - } - } - } - p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // @@ -1002,7 +982,10 @@ func inBodyIM(p *parser) bool { p.popUntil(buttonScope, a.P) p.addElement() case a.Button: - p.popUntil(defaultScope, a.Button) + if p.elementInScope(defaultScope, a.Button) { + p.generateImpliedEndTags() + p.popUntil(defaultScope, a.Button) + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false @@ -1040,7 +1023,18 @@ func inBodyIM(p *parser) bool { p.framesetOK = false p.im = inTableIM return true - case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: + case a.Area, a.Br, a.Embed, a.Img, a.Keygen, a.Wbr: + p.reconstructActiveFormattingElements() + p.addElement() + p.oe.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + case a.Input: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } + p.popUntil(defaultScope, a.Select) p.reconstructActiveFormattingElements() p.addElement() p.oe.pop() @@ -1061,7 +1055,13 @@ func inBodyIM(p *parser) bool { p.oe.pop() p.acknowledgeSelfClosingTag() case a.Hr: - p.popUntil(buttonScope, a.P) + if p.elementInScope(buttonScope, a.P) { + p.generateImpliedEndTags("p") + p.popUntil(defaultScope, a.P) + } + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + } p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() @@ -1095,13 +1095,30 @@ func inBodyIM(p *parser) bool { // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() case a.Select: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } else if p.popUntil(defaultScope, a.Select) { + return true + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false - p.im = inSelectIM return true - case a.Optgroup, a.Option: - if p.top().DataAtom == a.Option { + case a.Option: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags("optgroup") + // If oe has option element in scope, parse error? + } else if p.top().DataAtom == a.Option { + p.oe.pop() + } + p.reconstructActiveFormattingElements() + p.addElement() + case a.Optgroup: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + // If oe has option or optgroup element in scope, parse error? + } else if p.top().DataAtom == a.Option { p.oe.pop() } p.reconstructActiveFormattingElements() @@ -1149,7 +1166,12 @@ func inBodyIM(p *parser) bool { return false } return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Summary, a.Ul: + case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Select, a.Summary, a.Ul: + if !p.elementInScope(defaultScope, p.tok.DataAtom) { + // Ignore the token. + return true + } + p.generateImpliedEndTags() p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: if p.oe.contains(a.Template) { @@ -1488,17 +1510,6 @@ func inTableIM(p *parser) bool { } p.addElement() p.form = p.oe.pop() - case a.Select: - p.reconstructActiveFormattingElements() - switch p.top().DataAtom { - case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: - p.fosterParenting = true - } - p.addElement() - p.fosterParenting = false - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1547,12 +1558,6 @@ func inCaptionIM(p *parser) bool { p.clearActiveFormattingElements() p.im = inTableIM return false - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1762,12 +1767,6 @@ func inCellIM(p *parser) bool { } // Ignore the token. return true - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1798,118 +1797,6 @@ func inCellIM(p *parser) bool { return inBodyIM(p) } -// Section 12.2.6.4.16. -func inSelectIM(p *parser) bool { - switch p.tok.Type { - case TextToken: - p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) - case StartTagToken: - switch p.tok.DataAtom { - case a.Html: - return inBodyIM(p) - case a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - p.addElement() - case a.Optgroup: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - if p.top().DataAtom == a.Optgroup { - p.oe.pop() - } - p.addElement() - case a.Select: - if !p.popUntil(selectScope, a.Select) { - // Ignore the token. - return true - } - p.resetInsertionMode() - case a.Input, a.Keygen, a.Textarea: - if p.elementInScope(selectScope, a.Select) { - p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) - return false - } - // In order to properly ignore