Skip to content

Commit

Permalink
Add applyiptables4nattemplate chain element
Browse files Browse the repository at this point in the history
Signed-off-by: Sergey Shlyanin <sergey.shlyanin@xored.com>
  • Loading branch information
Sergey Shlyanin committed May 10, 2022
1 parent 396e655 commit 21c1649
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 4 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ module github.com/networkservicemesh/sdk-kernel
go 1.18

require (
github.com/edwarnicke/exechelper v1.0.2
github.com/golang/protobuf v1.5.2
github.com/networkservicemesh/api v1.3.0-rc.1.0.20220405210054-fbcde048efa5
github.com/networkservicemesh/api v1.3.2-0.20220509143420-a1414febd727
github.com/networkservicemesh/sdk v0.5.1-0.20220507173809-41196bdf49db
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0
Expand All @@ -22,6 +23,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.2.1 // indirect
github.com/go-logr/stdr v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
9 changes: 6 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/edwarnicke/exechelper v1.0.2 h1:dD49Ui2U0FBFxxhalnKw6vLS0P0TkgnXBRvKL/xmC5w=
github.com/edwarnicke/exechelper v1.0.2/go.mod h1:/T271jtNX/ND4De6pa2aRy2+8sNtyCDB1A2pp4M+fUs=
github.com/edwarnicke/serialize v1.0.7 h1:geX8vmyu8Ij2S5fFIXjy9gBDkKxXnrMIzMoDvV0Ddac=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand Down Expand Up @@ -61,6 +63,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
Expand All @@ -70,8 +74,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/networkservicemesh/api v1.3.0-rc.1.0.20220405210054-fbcde048efa5 h1:5zQY4PhShozvj/GFJS0dX6ocamAB9oWwEOJviAhGUaw=
github.com/networkservicemesh/api v1.3.0-rc.1.0.20220405210054-fbcde048efa5/go.mod h1:B6meq/SWjWR6bGXZdXPfbOeaBK+T1JayLdtEJQCsXKU=
github.com/networkservicemesh/api v1.3.2-0.20220509143420-a1414febd727 h1:IonbJeVgkwPLOrvDjd+c5Q2QPxtAUrwdLfAUv+8qk4w=
github.com/networkservicemesh/api v1.3.2-0.20220509143420-a1414febd727/go.mod h1:hOF2844BSstH1311oDMDgqqXS+kdc77htZNPRKl9mf8=
github.com/networkservicemesh/sdk v0.5.1-0.20220507173809-41196bdf49db h1:Ux4NNmYfPm/ayaR1DD5AWMXGSkPm+PdGHbvu34TAAno=
github.com/networkservicemesh/sdk v0.5.1-0.20220507173809-41196bdf49db/go.mod h1:G+NdPbtzcOWx1VljOj+L4kvZPQyYpiwCp84JYs+hGtc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down Expand Up @@ -197,7 +201,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
Expand Down
123 changes: 123 additions & 0 deletions pkg/kernel/networkservice/applyiptables4nattemplate/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) 2022 Xored Software Inc and others.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build linux
// +build linux

package applyiptables4nattemplate

import (
"bytes"
"fmt"
"log"
"os"
"strings"

"github.com/edwarnicke/exechelper"
"github.com/pkg/errors"
)

type applyIPTablesKey struct {
}

type iptableManagerImpl struct {
}

// IPTablesManager provides methods for iptables nat rules management
type IPTablesManager interface {
Get() (string, error)
Restore(string) error
Apply(string) error
}

func (m *iptableManagerImpl) Get() (string, error) {
cmdStr := "iptables-save"
buf := bytes.NewBuffer([]byte{})
err := exechelper.Run(cmdStr,
exechelper.WithStdout(buf),
exechelper.WithStderr(buf),
)
if err != nil {
err = errors.Wrapf(err, "%s", buf.String())
return "", err
}

return buf.String(), nil
}

func (m *iptableManagerImpl) Apply(rulesTemplate string) error {
rules := strings.Split(rulesTemplate, "\n")

for _, rule := range rules {
// Remove possible tabs from a multistring template
rule = strings.Trim(rule, "\t ")
arguments := strings.Split(rule, " ")
log.Println(rule)

cmdStr := "iptables -t nat"
buf := bytes.NewBuffer([]byte{})
err := exechelper.Run(cmdStr,
exechelper.WithArgs(arguments...),
exechelper.WithStdout(buf),
exechelper.WithStderr(buf),
)
if err != nil {
err = errors.Wrapf(err, "%s", buf.String())
return err
}
}

return nil
}

func (m *iptableManagerImpl) writeTmpRule(rules string) (string, error) {
fo, err := os.CreateTemp("/tmp", "rules-*")
if err != nil {
return "", err
}

defer func() { _ = fo.Close() }()
_, err = fo.WriteString(rules)
if err != nil {
return "", err
}

return fo.Name(), nil
}

func (m *iptableManagerImpl) Restore(rules string) error {
// Save rules to a temp file
tmpFile, err := m.writeTmpRule(rules)
if err != nil {
return err
}

defer func() { _ = os.Remove(tmpFile) }()

// Restore rules
cmdStr := fmt.Sprintf("iptables-restore %s", tmpFile)
buf := bytes.NewBuffer([]byte{})
err = exechelper.Run(cmdStr,
exechelper.WithStdout(buf),
exechelper.WithStderr(buf),
)
if err != nil {
err = errors.Wrapf(err, "%s", buf.String())
return err
}

return nil
}
115 changes: 115 additions & 0 deletions pkg/kernel/networkservice/applyiptables4nattemplate/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) 2022 Xored Software Inc and others.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build linux
// +build linux

// Package applyiptables4nattemplate provides chain element for setup iptables nat rules
package applyiptables4nattemplate

import (
"context"

"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"

"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/kernel"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
)

type iptablesServer struct {
manager IPTablesManager
}

// Option is an option pattern for applyiptables4nattemplate chain elements
type Option func(server *iptablesServer)

// WithIPTablesManager provides an option to change IPTablesManager for a chain element
func WithIPTablesManager(iptable IPTablesManager) Option {
return func(server *iptablesServer) {
server.manager = iptable
}
}

// NewServer - returns a new networkservice.NetworkServiceServer that modify IPTables rules
// by mechanism provided template on Request and rollbacks rules changes on Close
func NewServer(options ...Option) networkservice.NetworkServiceServer {
result := &iptablesServer{
manager: &iptableManagerImpl{},
}

for _, option := range options {
option(result)
}

return result
}

func (s *iptablesServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) {
ctxMap := metadata.Map(ctx, metadata.IsClient(s))
_, rulesWasApplied := ctxMap.Load(applyIPTablesKey{})

conn, err := next.Server(ctx).Request(ctx, request)
if err != nil {
return nil, err
}

// Check refresh requests
if !rulesWasApplied {
mechanism := kernel.ToMechanism(request.GetConnection().GetMechanism())
if mechanism != nil && mechanism.GetIPTables4NatTemplate() != "" {
rules, err := mechanism.EvaluateIPTables4NatTemplate(request.GetConnection())
if err != nil {
return nil, err
}

initialRules, err := s.manager.Get()
if err != nil {
return nil, err
}

ctxMap.Store(applyIPTablesKey{}, initialRules)

err = s.manager.Apply(rules)
if err != nil {
return nil, err
}
}
}

return conn, nil
}

func (s *iptablesServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) {
_, err := next.Server(ctx).Close(ctx, conn)

var restoreErr error
ctxMap := metadata.Map(ctx, metadata.IsClient(s))
if initialRules, rulesWasApplied := ctxMap.Load(applyIPTablesKey{}); rulesWasApplied {
restoreErr = s.manager.Restore(initialRules.(string))
}

if err != nil && restoreErr != nil {
return nil, errors.Wrap(err, restoreErr.Error())
}
if restoreErr != nil {
return nil, restoreErr
}

return &empty.Empty{}, err
}

0 comments on commit 21c1649

Please sign in to comment.