SRv6 masquerading proxy plugin

Change-Id: Ia65cf2c7b7fdf84c64e60af4cc815251022c53a9
Signed-off-by: Francois Clad <fclad@cisco.com>
This commit is contained in:
Francois Clad
2018-01-17 16:08:41 +01:00
committed by Florin Coras
parent 28227ad316
commit 39d91fe248
8 changed files with 794 additions and 0 deletions

View File

@ -159,6 +159,11 @@ M: Hongjun Ni <hongjun.ni@intel.com>
F: src/plugins/pppoe/
F: src/plugins/pppoe.am
Plugin - IPv6 Segment Routing Masquerading Proxy
M: Francois Clad <fclad@cisco.com>
F: src/plugins/srv6-am/
F: src/plugins/srv6_am.am
Plugin - IPv6 Segment Routing Static Proxy
M: Francois Clad <fclad@cisco.com>
F: src/plugins/srv6-as/

View File

@ -221,6 +221,7 @@ PLUGIN_ENABLED(marvell)
PLUGIN_ENABLED(memif)
PLUGIN_ENABLED(pppoe)
PLUGIN_ENABLED(sixrd)
PLUGIN_ENABLED(srv6am)
PLUGIN_ENABLED(srv6as)
PLUGIN_ENABLED(nat)
PLUGIN_ENABLED(stn)

View File

@ -83,6 +83,10 @@ if ENABLE_SIXRD_PLUGIN
include sixrd.am
endif
if ENABLE_SRV6AM_PLUGIN
include srv6_am.am
endif
if ENABLE_SRV6AS_PLUGIN
include srv6_as.am
endif

249
src/plugins/srv6-am/am.c Normal file
View File

@ -0,0 +1,249 @@
/*
* Copyright (c) 2015 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.
*/
/*
*------------------------------------------------------------------
* am.c - SRv6 Masquerading Proxy (AM) function
*------------------------------------------------------------------
*/
#include <vnet/vnet.h>
#include <vnet/adj/adj.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
#include <srv6-am/am.h>
unsigned char function_name[] = "SRv6-AM-plugin";
unsigned char keyword_str[] = "End.AM";
unsigned char def_str[] = "Endpoint to SR-unaware appliance via masquerading";
unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";
/*****************************************/
/* SRv6 LocalSID instantiation and removal functions */
static int
srv6_am_localsid_creation_fn (ip6_sr_localsid_t * localsid)
{
srv6_am_main_t *sm = &srv6_am_main;
srv6_am_localsid_t *ls_mem = localsid->plugin_mem;
adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
/* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
/* Retrieve the adjacency corresponding to the (OIF, next_hop) */
nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
VNET_LINK_IP6, &ls_mem->nh_addr,
ls_mem->sw_if_index_out);
if (nh_adj_index == ADJ_INDEX_INVALID)
return -5;
localsid->nh_adj = nh_adj_index;
/* Step 2: Prepare inbound policy for packets returning from the VNF */
/* Sanitise the SW_IF_INDEX */
if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
ls_mem->sw_if_index_in))
return -3;
vnet_sw_interface_t *sw = vnet_get_sw_interface (sm->vnet_main,
ls_mem->sw_if_index_in);
if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
return -3;
int ret = vnet_feature_enable_disable ("ip6-unicast", "srv6-am-rewrite",
ls_mem->sw_if_index_in, 1, 0, 0);
if (ret != 0)
return -1;
return 0;
}
static int
srv6_am_localsid_removal_fn (ip6_sr_localsid_t * localsid)
{
srv6_am_localsid_t *ls_mem = localsid->plugin_mem;
/* Remove hardware indirection (from sr_steering.c:137) */
int ret = vnet_feature_enable_disable ("ip6-unicast", "srv6-am-rewrite",
ls_mem->sw_if_index_in, 0, 0, 0);
if (ret != 0)
return -1;
/* Unlock (OIF, NHOP) adjacency (from sr_localsid.c:103) */
adj_unlock (localsid->nh_adj);
/* Clean up local SID memory */
clib_mem_free (localsid->plugin_mem);
return 0;
}
/**********************************/
/* SRv6 LocalSID format functions */
/*
* Prints nicely the parameters of a localsid
* Example: print "Table 5"
*/
u8 *
format_srv6_am_localsid (u8 * s, va_list * args)
{
srv6_am_localsid_t *ls_mem = va_arg (*args, void *);
vnet_main_t *vnm = vnet_get_main ();
return (format (s,
"Next-hop:\t%U\n"
"\tOutgoing iface: %U\n"
"\tIncoming iface: %U",
format_ip6_address, &ls_mem->nh_addr.ip6,
format_vnet_sw_if_index_name, vnm, ls_mem->sw_if_index_out,
format_vnet_sw_if_index_name, vnm, ls_mem->sw_if_index_in));
}
/*
* Process the parameters of a localsid
* Example: process from:
* sr localsid address cafe::1 behavior new_srv6_localsid 5
* everything from behavior on... so in this case 'new_srv6_localsid 5'
* Notice that it MUST match the keyword_str and params_str defined above.
*/
uword
unformat_srv6_am_localsid (unformat_input_t * input, va_list * args)
{
void **plugin_mem_p = va_arg (*args, void **);
srv6_am_localsid_t *ls_mem;
vnet_main_t *vnm = vnet_get_main ();
ip46_address_t nh_addr;
u32 sw_if_index_out;
u32 sw_if_index_in;
if (unformat (input, "end.am nh %U oif %U iif %U",
unformat_ip6_address, &nh_addr.ip6,
unformat_vnet_sw_interface, vnm, &sw_if_index_out,
unformat_vnet_sw_interface, vnm, &sw_if_index_in))
{
/* Allocate a portion of memory */
ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
/* Set to zero the memory */
memset (ls_mem, 0, sizeof *ls_mem);
/* Our brand-new car is ready */
clib_memcpy (&ls_mem->nh_addr.ip6, &nh_addr.ip6,
sizeof (ip6_address_t));
ls_mem->sw_if_index_out = sw_if_index_out;
ls_mem->sw_if_index_in = sw_if_index_in;
/* Dont forget to add it to the localsid */
*plugin_mem_p = ls_mem;
return 1;
}
return 0;
}
/*************************/
/* SRv6 LocalSID FIB DPO */
static u8 *
format_srv6_am_dpo (u8 * s, va_list * args)
{
index_t index = va_arg (*args, index_t);
CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
return (format (s, "SR: dynamic_proxy_index:[%u]", index));
}
void
srv6_am_dpo_lock (dpo_id_t * dpo)
{
}
void
srv6_am_dpo_unlock (dpo_id_t * dpo)
{
}
const static dpo_vft_t srv6_am_vft = {
.dv_lock = srv6_am_dpo_lock,
.dv_unlock = srv6_am_dpo_unlock,
.dv_format = format_srv6_am_dpo,
};
const static char *const srv6_am_ip6_nodes[] = {
"srv6-am-localsid",
NULL,
};
const static char *const *const srv6_am_nodes[DPO_PROTO_NUM] = {
[DPO_PROTO_IP6] = srv6_am_ip6_nodes,
};
/**********************/
static clib_error_t *
srv6_am_init (vlib_main_t * vm)
{
srv6_am_main_t *sm = &srv6_am_main;
int rv = 0;
sm->vlib_main = vm;
sm->vnet_main = vnet_get_main ();
/* Create DPO */
sm->srv6_am_dpo_type = dpo_register_new_type (&srv6_am_vft, srv6_am_nodes);
/* Register SRv6 LocalSID */
rv = sr_localsid_register_function (vm,
function_name,
keyword_str,
def_str,
params_str,
&sm->srv6_am_dpo_type,
format_srv6_am_localsid,
unformat_srv6_am_localsid,
srv6_am_localsid_creation_fn,
srv6_am_localsid_removal_fn);
if (rv < 0)
clib_error_return (0, "SRv6 LocalSID function could not be registered.");
else
sm->srv6_localsid_behavior_id = rv;
return 0;
}
/* *INDENT-OFF* */
VNET_FEATURE_INIT (srv6_am_rewrite, static) =
{
.arc_name = "ip6-unicast",
.node_name = "srv6-am-rewrite",
.runs_before = 0,
};
VLIB_INIT_FUNCTION (srv6_am_init);
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
.description = "Masquerading SRv6 proxy",
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

67
src/plugins/srv6-am/am.h Normal file
View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2015 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_srv6_am_h__
#define __included_srv6_am_h__
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vnet/srv6/sr.h>
#include <vnet/srv6/sr_packet.h>
#include <vppinfra/error.h>
#include <vppinfra/elog.h>
typedef struct
{
u16 msg_id_base; /**< API message ID base */
vlib_main_t *vlib_main; /**< [convenience] vlib main */
vnet_main_t *vnet_main; /**< [convenience] vnet main */
dpo_type_t srv6_am_dpo_type; /**< DPO type */
u32 srv6_localsid_behavior_id; /**< SRv6 LocalSID behavior number */
} srv6_am_main_t;
/*
* This is the memory that will be stored per each localsid
* the user instantiates
*/
typedef struct
{
ip46_address_t nh_addr; /**< Proxied device address */
u32 sw_if_index_out; /**< Outgoing iface to proxied device */
u32 sw_if_index_in; /**< Incoming iface from proxied device */
} srv6_am_localsid_t;
srv6_am_main_t srv6_am_main;
format_function_t format_srv6_am_localsid;
unformat_function_t unformat_srv6_am_localsid;
void srv6_am_dpo_lock (dpo_id_t * dpo);
void srv6_am_dpo_unlock (dpo_id_t * dpo);
extern vlib_node_registration_t srv6_am_localsid_node;
#endif /* __included_srv6_am_h__ */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,91 @@
# SRv6 endpoint to SR-unaware appliance via masquerading (End.AM) {#srv6_am_plugin_doc}
## Overview
The "Endpoint to SR-unaware appliance via masquerading" (End.AM) is a two-parts
function for processing SRv6 **inserted** traffic on behalf of an SR-unaware
appliance. The first part decrements the Segments Left value and **replaces the
IPv6 Destination Address with the last segment in the SRH**, while the second
restores the IPv6 Destination Address with the active segment in the traffic
coming back from the appliance.
In this scenario, we assume that the appliance can only inspect, drop or perform
limited changes to the packets. In particular, the appliance must not change the
IP Destination Address of the packet, terminate a transport connection nor
generate arbitrary packets. For example, Firewalls, Intrusion Detection Systems,
Deep Packet Inspectors are among the appliances that can be supported in this
scenario.
## Pseudo-code
When instantiating an End.AM SID, the following parameters are required:
- APP-ADDR: IP or Ethernet address of the appliance
- IFACE-OUT: local interface for sending traffic towards the appliance
- IFACE-IN: local interface receiving the traffic coming back from the appliance
Packets can be sent to and received from an appliance on the same interface
(IFACE-IN = IFACE-OUT).
### Masquerading
Upon receiving a packet destined to S, where S is a local End.AM SID, a node N
does:
IF NH=SRH & SL > 0 THEN ;; Ref1
Decrement SL
Write the last SID in the DA
Forward the packet on IFACE-OUT
ELSE
Drop the packet
**Ref1:** an End.AM must not be the last SID.
### De-masquerading
Upon receiving a non-link-local IPv6 packet on IFACE-IN, a node N does:
IF NH=SRH THEN
Replace IP DA with SRH[SL]
Lookup DA in the appropriate table and proceed accordingly
De-masquerading is a policy attached to IFACE-IN that intercepts all packets
coming back from the appliance and restores the destination address. This
occurs before any lookup on the packet destination address (e.g. in "My Local
SIDs" table or in the FIB) is performed.
## Benefits
The End.AM masquerading function brings the following benefits:
1. The appliance receives a packet with the source and destination addresses
respectively set as the original source and the final destination.
2. The appliance does not try and inspect the SRH, as RFC2460 specifies that
routing extension headers are not examined or processed by transit nodes.
## Limitations
An End.AM SID may be present in any number of segment lists at the same time.
However, since the returning traffic from the appliance is processed based on
the receiving interface (IFACE-IN), this interface may only be bound to a single
End.AM SID at a time.
In the case of a bi-directional service chain, the same End.AM SID and receiving
interface (IFACE-IN) may be used in both directions.
## Configuration
The following CLI instantiates a new End.AM segment that sends masqueraded
traffic on interface `IFACE-OUT` towards an appliance at address `APP-ADDR` and
restores the active segment in the IPv6 header of the packets coming back on
interface `IFACE-IN`.
sr localsid address SID behavior end.am nh APP-ADDR oif IFACE-OUT iif IFACE-IN
For example, the following command configures the SID `1::A1` with an End.AM
function for sending traffic on interface `GigabitEthernet0/8/0` to the appliance at
address `A1::`, and receiving it back on interface `GigabitEthernet0/9/0`.
sr localsid address 1::A1 behavior end.am nh A1:: oif GigabitEthernet0/8/0 iif GigabitEthernet0/9/0

355
src/plugins/srv6-am/node.c Normal file

File diff suppressed because it is too large Load Diff

22
src/plugins/srv6_am.am Normal file
View File

@ -0,0 +1,22 @@
# Copyright (c) 2016 Cisco Systems, Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
vppplugins_LTLIBRARIES += srv6am_plugin.la
srv6am_plugin_la_SOURCES = \
srv6-am/am.c \
srv6-am/node.c
noinst_HEADERS += srv6-am/am.h
# vi:syntax=automake