aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/acl-plugin/acl/l2sess.h
diff options
context:
space:
mode:
authorAndrew Yourtchenko <ayourtch@gmail.com>2016-12-05 16:25:28 +0000
committerDamjan Marion <dmarion.lists@gmail.com>2016-12-06 08:18:53 +0000
commitb09167f33d3c79e7ccc27e0fc484cc5fbcdb9943 (patch)
tree9824d4efba7af3e6eb40507f83d4a1e76ae392ef /plugins/acl-plugin/acl/l2sess.h
parent65209ed18c0bdc4e8d3d4a2ebbcd6cf34b68bb43 (diff)
acl: The ACL plugin.
This is the commit from the accumulated work in the github ACL branch, to move it to gerrit. Change-Id: I85a6b0df0d3dd3c3c7588e92a1e22c553e4b6ef7 Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
Diffstat (limited to 'plugins/acl-plugin/acl/l2sess.h')
-rw-r--r--plugins/acl-plugin/acl/l2sess.h150
1 files changed, 150 insertions, 0 deletions
diff --git a/plugins/acl-plugin/acl/l2sess.h b/plugins/acl-plugin/acl/l2sess.h
new file mode 100644
index 00000000000..db899917113
--- /dev/null
+++ b/plugins/acl-plugin/acl/l2sess.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * 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.
+ */
+#ifndef __included_l2sess_h__
+#define __included_l2sess_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <vppinfra/hash.h>
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+#include <vppinfra/timing_wheel.h>
+
+#include <vnet/l2/l2_output.h>
+#include <vnet/l2/l2_input.h>
+
+#define _(node_name, node_var, is_out, is_ip6, is_track)
+#undef _
+#define foreach_l2sess_node \
+ _("aclp-l2s-input-ip4-add", l2sess_in_ip4_add, 0, 0, 0) \
+ _("aclp-l2s-input-ip6-add", l2sess_in_ip6_add, 0, 1, 0) \
+ _("aclp-l2s-output-ip4-add", l2sess_out_ip4_add, 1, 0, 0) \
+ _("aclp-l2s-output-ip6-add", l2sess_out_ip6_add, 1, 1, 0) \
+ _("aclp-l2s-input-ip4-track", l2sess_in_ip4_track, 0, 0, 1) \
+ _("aclp-l2s-input-ip6-track", l2sess_in_ip6_track, 0, 1, 1) \
+ _("aclp-l2s-output-ip4-track",l2sess_out_ip4_track, 1, 0, 1) \
+ _("aclp-l2s-output-ip6-track", l2sess_out_ip6_track, 1, 1, 1)
+
+#define _(node_name, node_var, is_out, is_ip6, is_track) \
+ extern vlib_node_registration_t node_var;
+foreach_l2sess_node
+#undef _
+
+#define TCP_FLAG_FIN 0x01
+#define TCP_FLAG_SYN 0x02
+#define TCP_FLAG_RST 0x04
+#define TCP_FLAG_PUSH 0x08
+#define TCP_FLAG_ACK 0x10
+#define TCP_FLAG_URG 0x20
+#define TCP_FLAG_ECE 0x40
+#define TCP_FLAG_CWR 0x80
+#define TCP_FLAGS_RSTFINACKSYN (TCP_FLAG_RST + TCP_FLAG_FIN + TCP_FLAG_SYN + TCP_FLAG_ACK)
+#define TCP_FLAGS_ACKSYN (TCP_FLAG_SYN + TCP_FLAG_ACK)
+
+typedef struct {
+ ip46_address_t addr;
+ u64 active_time;
+ u64 n_packets;
+ u64 n_bytes;
+ u16 port;
+} l2s_session_side_t;
+
+enum {
+ L2S_SESSION_SIDE_IN = 0,
+ L2S_SESSION_SIDE_OUT,
+ L2S_N_SESSION_SIDES
+};
+
+typedef struct {
+ u64 create_time;
+ l2s_session_side_t side[L2S_N_SESSION_SIDES];
+ u8 l4_proto;
+ u8 is_ip6;
+ u16 tcp_flags_seen; /* u16 because of two sides */
+} l2s_session_t;
+
+#define PROD
+#ifdef PROD
+#define UDP_SESSION_IDLE_TIMEOUT_SEC 600
+#define TCP_SESSION_IDLE_TIMEOUT_SEC (3600*24)
+#define TCP_SESSION_TRANSIENT_TIMEOUT_SEC 120
+#else
+#define UDP_SESSION_IDLE_TIMEOUT_SEC 15
+#define TCP_SESSION_IDLE_TIMEOUT_SEC 15
+#define TCP_SESSION_TRANSIENT_TIMEOUT_SEC 5
+#endif
+
+typedef struct {
+ /*
+ * the next two fields are present for all nodes, but
+ * only one of them is used per node - depending
+ * on whether the node is an input or output one.
+ */
+#define _(node_name, node_var, is_out, is_ip6, is_track) \
+ u32 node_var ## _input_next_node_index[32]; \
+ l2_output_next_nodes_st node_var ## _next_nodes;
+foreach_l2sess_node
+#undef _
+ l2_output_next_nodes_st output_next_nodes;
+
+ /* Next indices of the tracker nodes */
+ u32 next_slot_track_node_by_is_ip6_is_out[2][2];
+
+ /*
+ * Pairing of "forward" and "reverse" tables by table index.
+ * Each relationship has two entries - for one and the other table,
+ * so it is bidirectional.
+ */
+
+ u32 *fwd_to_rev_by_table_index;
+
+ /*
+ * The vector of per-interface session pools
+ */
+
+ l2s_session_t *sessions;
+
+ /* The session timeouts */
+ u64 tcp_session_transient_timeout;
+ u64 tcp_session_idle_timeout;
+ u64 udp_session_idle_timeout;
+
+ /* Timing wheel to time out the idle sessions */
+ timing_wheel_t timing_wheel;
+ u32 *data_from_advancing_timing_wheel;
+ u64 timer_wheel_next_expiring_time;
+ u64 timer_wheel_tick;
+
+ /* convenience */
+ vlib_main_t * vlib_main;
+ vnet_main_t * vnet_main;
+ ethernet_main_t * ethernet_main;
+
+ /* Counter(s) */
+ u64 counter_attempted_delete_free_session;
+} l2sess_main_t;
+
+l2sess_main_t l2sess_main;
+
+/* Just exposed for acl.c */
+
+void
+l2sess_vlib_plugin_register (vlib_main_t * vm, void * hh,
+ int from_early_init);
+
+
+#endif /* __included_l2sess_h__ */
> 405 406 407 408 409
package main

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"path/filepath"

	"git.fd.io/govpp.git/api"
	"github.com/edwarnicke/exechelper"
	"github.com/edwarnicke/govpp/binapi/af_packet"
	"github.com/edwarnicke/govpp/binapi/ethernet_types"
	interfaces "github.com/edwarnicke/govpp/binapi/interface"
	"github.com/edwarnicke/govpp/binapi/interface_types"
	ip_types "github.com/edwarnicke/govpp/binapi/ip_types"
	"github.com/edwarnicke/govpp/binapi/session"
	"github.com/edwarnicke/govpp/binapi/tapv2"
	"github.com/edwarnicke/govpp/binapi/vlib"
	"github.com/edwarnicke/vpphelper"
)

var (
	workDir, _ = os.Getwd()
)

type ConfFn func(context.Context, api.Connection) error

type Actions struct {
}

func configureProxyTcp(ifName0, ipAddr0, ifName1, ipAddr1 string) ConfFn {
	return func(ctx context.Context,
		vppConn api.Connection) error {

		_, err := configureAfPacket(ctx, vppConn, ifName0, ipAddr0)
		if err != nil {
			fmt.Printf("failed to create af packet: %v", err)
			return err
		}
		_, err = configureAfPacket(ctx, vppConn, ifName1, ipAddr1)
		if err != nil {
			fmt.Printf("failed to create af packet: %v", err)
			return err
		}
		return nil
	}
}

func (a *Actions) RunHttpCliSrv(args []string) *ActionResult {
	cmd := fmt.Sprintf("http cli server")
	return ApiCliInband(workDir, cmd)
}

func (a *Actions) RunHttpCliCln(args []string) *ActionResult {
	cmd := fmt.Sprintf("http cli client uri http://10.10.10.1/80 query %s", getArgs())
	fmt.Println(cmd)
	return ApiCliInband(workDir, cmd)
}

func (a *Actions) ConfigureVppProxy(args []string) *ActionResult {
	ctx, cancel := newVppContext()
	defer cancel()

	con, vppErrCh := vpphelper.StartAndDialContext(ctx,
		vpphelper.WithVppConfig(configTemplate),
		vpphelper.WithRootDir(workDir))
	exitOnErrCh(ctx, cancel, vppErrCh)

	confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24")
	err := confFn(ctx, con)
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("configuration failed"))
	}
	writeSyncFile(OkResult())
	<-ctx.Done()
	return nil
}

func (a *Actions) ConfigureEnvoyProxy(args []string) *ActionResult {
	var startup Stanza
	startup.
		NewStanza("session").
		Append("enable").
		Append("use-app-socket-api").
		Append("evt_qs_memfd_seg").
		Append("event-queue-length 100000").Close()
	ctx, cancel := newVppContext()
	defer cancel()

	con, vppErrCh := vpphelper.StartAndDialContext(ctx,
		vpphelper.WithVppConfig(configTemplate+startup.ToString()),
		vpphelper.WithRootDir(workDir))
	exitOnErrCh(ctx, cancel, vppErrCh)

	confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24")
	err := confFn(ctx, con)
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("configuration failed"))
	}
	err0 := exechelper.Run("chmod 777 -R " + workDir)
	if err0 != nil {
		return NewActionResult(err, ActionResultWithDesc("setting permissions failed"))
	}
	writeSyncFile(OkResult())
	<-ctx.Done()
	return nil
}

func getArgs() string {
	s := ""
	for i := 2; i < len(os.Args); i++ {
		s = s + " " + os.Args[i]
	}
	return s
}

func ApiCliInband(root, cmd string) *ActionResult {
	ctx, _ := newVppContext()
	con := vpphelper.DialContext(ctx, filepath.Join(root, "/var/run/vpp/api.sock"))
	cliInband := vlib.CliInband{Cmd: cmd}
	cliInbandReply, err := vlib.NewServiceClient(con).CliInband(ctx, &cliInband)
	return NewActionResult(err, ActionResultWithStdout(cliInbandReply.Reply))
}

func (a *Actions) RunEchoClient(args []string) *ActionResult {
	outBuff := bytes.NewBuffer([]byte{})
	errBuff := bytes.NewBuffer([]byte{})

	cmd := fmt.Sprintf("vpp_echo client socket-name %s/var/run/app_ns_sockets/2 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2])
	err := exechelper.Run(cmd,
		exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff),
		exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr))

	return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())),
		ActionResultWithStderr(string(errBuff.String())))
}

func (a *Actions) RunEchoServer(args []string) *ActionResult {
	cmd := fmt.Sprintf("vpp_echo server TX=RX socket-name %s/var/run/app_ns_sockets/1 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2])
	errCh := exechelper.Start(cmd)
	select {
	case err := <-errCh:
		writeSyncFile(NewActionResult(err, ActionResultWithDesc("echo_server: ")))
	default:
	}
	writeSyncFile(OkResult())
	return nil
}

func (a *Actions) RunEchoSrvInternal(args []string) *ActionResult {
	cmd := fmt.Sprintf("test echo server %s uri tcp://10.10.10.1/1234", getArgs())
	return ApiCliInband(workDir, cmd)
}

func (a *Actions) RunEchoClnInternal(args []string) *ActionResult {
	cmd := fmt.Sprintf("test echo client %s uri tcp://10.10.10.1/1234", getArgs())
	return ApiCliInband(workDir, cmd)
}

func (a *Actions) RunVclEchoServer(args []string) *ActionResult {
	f, err := os.Create("vcl_1.conf")
	if err != nil {
		return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
	}
	socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/1", workDir)
	fmt.Fprintf(f, vclTemplate, socketPath, "1")
	f.Close()

	os.Setenv("VCL_CONFIG", "./vcl_1.conf")
	cmd := fmt.Sprintf("vcl_test_server -p %s 12346", args[2])
	errCh := exechelper.Start(cmd)
	select {
	case err := <-errCh:
		writeSyncFile(NewActionResult(err, ActionResultWithDesc("vcl_test_server: ")))
	default:
	}
	writeSyncFile(OkResult())
	return nil
}

func (a *Actions) RunVclEchoClient(args []string) *ActionResult {
	outBuff := bytes.NewBuffer([]byte{})
	errBuff := bytes.NewBuffer([]byte{})

	f, err := os.Create("vcl_2.conf")
	if err != nil {
		return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
	}
	socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/2", workDir)
	fmt.Fprintf(f, vclTemplate, socketPath, "2")
	f.Close()

	os.Setenv("VCL_CONFIG", "./vcl_2.conf")
	cmd := fmt.Sprintf("vcl_test_client -U -p %s 10.10.10.1 12346", args[2])
	err = exechelper.Run(cmd,
		exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff),
		exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr))

	return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())),
		ActionResultWithStderr(string(errBuff.String())))
}

func configure2vethsTopo(ifName, interfaceAddress, namespaceId string, secret uint64, optionalHardwareAddress ...string) ConfFn {
	return func(ctx context.Context,
		vppConn api.Connection) error {

		var swIfIndex interface_types.InterfaceIndex
		var err error
		if optionalHardwareAddress == nil {
			swIfIndex, err = configureAfPacket(ctx, vppConn, ifName, interfaceAddress)
		} else {
			swIfIndex, err = configureAfPacket(ctx, vppConn, ifName, interfaceAddress, optionalHardwareAddress[0])
		}
		if err != nil {
			fmt.Printf("failed to create af packet: %v", err)
		}
		_, er := session.NewServiceClient(vppConn).AppNamespaceAddDelV2(ctx, &session.AppNamespaceAddDelV2{
			Secret:      secret,
			SwIfIndex:   swIfIndex,
			NamespaceID: namespaceId,
		})
		if er != nil {
			fmt.Printf("add app namespace: %v", err)
			return err
		}

		_, er1 := session.NewServiceClient(vppConn).SessionEnableDisable(ctx, &session.SessionEnableDisable{
			IsEnable: true,
		})
		if er1 != nil {
			fmt.Printf("session enable %v", err)
			return err
		}
		return nil
	}
}

func (a *Actions) Configure2Veths(args []string) *ActionResult {
	var startup Stanza
	startup.
		NewStanza("session").
		Append("enable").
		Append("use-app-socket-api").Close()

	ctx, cancel := newVppContext()
	defer cancel()

	vppConfig, err := deserializeVppConfig(args[2])
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("deserializing configuration failed"))
	}

	con, vppErrCh := vpphelper.StartAndDialContext(ctx,
		vpphelper.WithVppConfig(vppConfig.getTemplate()+startup.ToString()),
		vpphelper.WithRootDir(workDir))
	exitOnErrCh(ctx, cancel, vppErrCh)

	var fn func(context.Context, api.Connection) error
	switch vppConfig.Variant {
	case "srv":
		fn = configure2vethsTopo("vppsrv", "10.10.10.1/24", "1", 1)
	case "srv-with-preset-hw-addr":
		fn = configure2vethsTopo("vppsrv", "10.10.10.1/24", "1", 1, "00:00:5e:00:53:01")
	case "cln":
		fallthrough
	default:
		fn = configure2vethsTopo("vppcln", "10.10.10.2/24", "2", 2)
	}
	err = fn(ctx, con)
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("configuration failed"))
	}
	writeSyncFile(OkResult())
	<-ctx.Done()
	return nil
}

func configureAfPacket(ctx context.Context, vppCon api.Connection,
	name, interfaceAddress string, optionalHardwareAddress ...string) (interface_types.InterfaceIndex, error) {
	var err error
	ifaceClient := interfaces.NewServiceClient(vppCon)
	afPacketCreate := af_packet.AfPacketCreateV2{
		UseRandomHwAddr: true,
		HostIfName:      name,
		NumRxQueues:     1,
	}
	if len(optionalHardwareAddress) > 0 {
		afPacketCreate.HwAddr, err = ethernet_types.ParseMacAddress(optionalHardwareAddress[0])
		if err != nil {
			fmt.Printf("failed to parse mac address: %v", err)
			return 0, err
		}
		afPacketCreate.UseRandomHwAddr = false
	}
	afPacketCreateRsp, err := af_packet.NewServiceClient(vppCon).AfPacketCreateV2(ctx, &afPacketCreate)
	if err != nil {
		fmt.Printf("failed to create af packet: %v", err)
		return 0, err
	}
	_, err = ifaceClient.SwInterfaceSetFlags(ctx, &interfaces.SwInterfaceSetFlags{
		SwIfIndex: afPacketCreateRsp.SwIfIndex,
		Flags:     interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
	})
	if err != nil {
		fmt.Printf("set interface state up failed: %v\n", err)
		return 0, err
	}
	ipPrefix, err := ip_types.ParseAddressWithPrefix(interfaceAddress)
	if err != nil {
		fmt.Printf("parse ip address %v\n", err)
		return 0, err
	}
	ipAddress := &interfaces.SwInterfaceAddDelAddress{
		IsAdd:     true,
		SwIfIndex: afPacketCreateRsp.SwIfIndex,
		Prefix:    ipPrefix,
	}
	_, errx := ifaceClient.SwInterfaceAddDelAddress(ctx, ipAddress)
	if errx != nil {
		fmt.Printf("add ip address %v\n", err)
		return 0, err
	}
	return afPacketCreateRsp.SwIfIndex, nil
}

func (a *Actions) ConfigureHttpTps(args []string) *ActionResult {
	ctx, cancel := newVppContext()
	defer cancel()
	con, vppErrCh := vpphelper.StartAndDialContext(ctx,
		vpphelper.WithVppConfig(configTemplate))
	exitOnErrCh(ctx, cancel, vppErrCh)

	confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24")
	err := confFn(ctx, con)
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("configuration failed"))
	}

	_, err = session.NewServiceClient(con).SessionEnableDisable(ctx, &session.SessionEnableDisable{
		IsEnable: true,
	})
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("configuration failed"))
	}
	Vppcli("", "http tps uri tcp://0.0.0.0/8080")
	writeSyncFile(OkResult())
	<-ctx.Done()
	return nil
}

func (a *Actions) ConfigureTap(args []string) *ActionResult {
	var startup Stanza
	startup.
		NewStanza("session").
		Append("enable").
		Append("use-app-socket-api").Close()

	ctx, cancel := newVppContext()
	defer cancel()
	con, vppErrCh := vpphelper.StartAndDialContext(ctx,
		vpphelper.WithRootDir(workDir),
		vpphelper.WithVppConfig(configTemplate+startup.ToString()))
	exitOnErrCh(ctx, cancel, vppErrCh)
	ifaceClient := interfaces.NewServiceClient(con)

	pref, err := ip_types.ParseIP4Prefix("10.10.10.2/24")
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("failed to parse ip4 address"))
	}
	createTapReply, err := tapv2.NewServiceClient(con).TapCreateV2(ctx, &tapv2.TapCreateV2{
		HostIfNameSet:    true,
		HostIfName:       "tap0",
		HostIP4PrefixSet: true,
		HostIP4Prefix:    ip_types.IP4AddressWithPrefix(pref),
	})
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("failed to configure tap"))
	}
	ipPrefix, err := ip_types.ParseAddressWithPrefix("10.10.10.1/24")
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("parsing ip address failed"))
	}
	ipAddress := &interfaces.SwInterfaceAddDelAddress{
		IsAdd:     true,
		SwIfIndex: createTapReply.SwIfIndex,
		Prefix:    ipPrefix,
	}
	_, errx := ifaceClient.SwInterfaceAddDelAddress(ctx, ipAddress)
	if errx != nil {
		return NewActionResult(err, ActionResultWithDesc("configuring ip address failed"))
	}
	_, err = ifaceClient.SwInterfaceSetFlags(ctx, &interfaces.SwInterfaceSetFlags{
		SwIfIndex: createTapReply.SwIfIndex,
		Flags:     interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
	})
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("failed to set interface state"))
	}
	_, err = session.NewServiceClient(con).SessionEnableDisable(ctx, &session.SessionEnableDisable{
		IsEnable: true,
	})
	if err != nil {
		return NewActionResult(err, ActionResultWithDesc("configuration failed"))
	}
	writeSyncFile(OkResult())
	<-ctx.Done()
	return nil
}