[ovs-dev] [PATCH 7/7] OpenFlow-level flow-based tunneling support.
Jarno Rajahalme
jarno.rajahalme at nsn.com
Thu Apr 18 15:07:45 UTC 2013
Adds tun_src and tun_dst match and set capabilities via new NXM fields
NXM_NX_TUN_IPV4_SRC and NXM_NX_TUN_IPV4_DST. This allows management of
large number of tunnels via flow tables, without requiring the tunnels
to be pre-configured.
NXM_NX_TUN_IPV4_SRC and NXM_NX_TUN_IPV4_DST are also included in the
packet_in metadata, when available, so that they are available to
OpenFlow controllers.
Flow-based tunnels can be configured with options remote_ip=flow and
local_ip=flow. local_ip=flow requires remote_ip=flow. When set, the
tunnel remote IP address and/or local IP address is set from the flow,
instead of the tunnel configuration.
Example:
$ ovs-vsctl add-port br0 gre -- set Interface gre ofport_request=1 type=gre options:remote_ip=flow options:key=flow
$ ovs-ofctl add-flow br0 "in_port=LOCAL actions=set_tunnel:1,set_field:192.168.0.1->tun_dst,output:1"
$ ovs-ofctl add-flow br0 "in_port=1 tun_src=192.168.0.1 tun_id=1 actions=LOCAL"
Signed-off-by: Jarno Rajahalme <jarno.rajahalme at nsn.com>
---
include/openflow/nicira-ext.h | 3 +++
lib/flow.c | 2 ++
lib/flow.h | 2 ++
lib/meta-flow.c | 20 +++++++++++---------
lib/netdev-vport.c | 21 ++++++++++++++++++---
lib/netdev.h | 2 ++
lib/nx-match.c | 8 +++++++-
lib/ofp-print.c | 8 ++++++++
lib/ofp-util.c | 29 +++++++++++++++--------------
ofproto/tunnel.c | 38 ++++++++++++++++++++++++++++++++++----
tests/ofproto.at | 27 +++++++++++++++++++++++++++
tests/tunnel.at | 28 ++++++++++++++++++++++++++++
12 files changed, 157 insertions(+), 31 deletions(-)
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 54fc4f9..a01ce27 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1745,6 +1745,9 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
#define NXM_NX_COOKIE NXM_HEADER (0x0001, 30, 8)
#define NXM_NX_COOKIE_W NXM_HEADER_W(0x0001, 30, 8)
+#define NXM_NX_TUN_IPV4_SRC NXM_HEADER (0x0001, 31, 4)
+#define NXM_NX_TUN_IPV4_DST NXM_HEADER (0x0001, 32, 4)
+
/* ## --------------------- ## */
/* ## Requests and replies. ## */
/* ## --------------------- ## */
diff --git a/lib/flow.c b/lib/flow.c
index 678b8f0..9a5f4b1 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -495,6 +495,8 @@ flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
fmd->tun_id = flow->tunnel.tun_id;
+ fmd->tun_src = flow->tunnel.ip_src;
+ fmd->tun_dst = flow->tunnel.ip_dst;
fmd->metadata = flow->metadata;
memcpy(fmd->regs, flow->regs, sizeof fmd->regs);
fmd->in_port = flow->in_port;
diff --git a/lib/flow.h b/lib/flow.h
index 6e169d6..0fdf4ef 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -118,6 +118,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 160 &&
/* Represents the metadata fields of struct flow. */
struct flow_metadata {
ovs_be64 tun_id; /* Encapsulating tunnel ID. */
+ ovs_be32 tun_src; /* Tunnel outer IPv4 src addr */
+ ovs_be32 tun_dst; /* Tunnel outer IPv4 dst addr */
ovs_be64 metadata; /* OpenFlow 1.1+ metadata field. */
uint32_t regs[FLOW_N_REGS]; /* Registers. */
uint16_t in_port; /* OpenFlow port or zero. */
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 6f7a3aa..a4edc8f 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -57,21 +57,21 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
}, {
MFF_TUN_SRC, "tun_src", NULL,
MF_FIELD_SIZES(be32),
- MFM_NONE,
+ MFM_FULLY,
MFS_IPV4,
MFP_NONE,
- false,
- 0, NULL,
- 0, NULL,
+ true,
+ NXM_NX_TUN_IPV4_SRC, "NXM_NX_TUN_IPV4_SRC",
+ NXM_NX_TUN_IPV4_SRC, "NXM_NX_TUN_IPV4_SRC",
}, {
MFF_TUN_DST, "tun_dst", NULL,
MF_FIELD_SIZES(be32),
- MFM_NONE,
+ MFM_FULLY,
MFS_IPV4,
MFP_NONE,
- false,
- 0, NULL,
- 0, NULL,
+ true,
+ NXM_NX_TUN_IPV4_DST, "NXM_NX_TUN_IPV4_DST",
+ NXM_NX_TUN_IPV4_DST, "NXM_NX_TUN_IPV4_DST",
}, {
MFF_TUN_FLAGS, "tun_flags", NULL,
MF_FIELD_SIZES(be16),
@@ -667,9 +667,11 @@ bool
mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
{
switch (mf->id) {
- case MFF_TUN_ID:
case MFF_TUN_SRC:
+ return !wc->masks.tunnel.ip_src;
case MFF_TUN_DST:
+ return !wc->masks.tunnel.ip_dst;
+ case MFF_TUN_ID:
case MFF_TUN_TOS:
case MFF_TUN_TTL:
case MFF_TUN_FLAGS:
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 4aa8bb0..80f78e0 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -327,7 +327,10 @@ set_tunnel_config(struct netdev_dev *dev_, const struct smap *args)
SMAP_FOR_EACH (node, args) {
if (!strcmp(node->key, "remote_ip")) {
struct in_addr in_addr;
- if (lookup_ip(node->value, &in_addr)) {
+ if (!strcmp(node->value, "flow")) {
+ tnl_cfg.ip_dst_flow = true;
+ tnl_cfg.ip_dst = htonl(0);
+ } else if (lookup_ip(node->value, &in_addr)) {
VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
} else if (ip_is_multicast(in_addr.s_addr)) {
VLOG_WARN("%s: multicast remote_ip="IP_FMT" not allowed",
@@ -338,7 +341,10 @@ set_tunnel_config(struct netdev_dev *dev_, const struct smap *args)
}
} else if (!strcmp(node->key, "local_ip")) {
struct in_addr in_addr;
- if (lookup_ip(node->value, &in_addr)) {
+ if (!strcmp(node->value, "flow")) {
+ tnl_cfg.ip_src_flow = true;
+ tnl_cfg.ip_src = htonl(0);
+ } else if (lookup_ip(node->value, &in_addr)) {
VLOG_WARN("%s: bad %s 'local_ip'", name, type);
} else {
tnl_cfg.ip_src = in_addr.s_addr;
@@ -445,11 +451,16 @@ set_tunnel_config(struct netdev_dev *dev_, const struct smap *args)
}
}
- if (!tnl_cfg.ip_dst) {
+ if (!tnl_cfg.ip_dst && !tnl_cfg.ip_dst_flow) {
VLOG_ERR("%s: %s type requires valid 'remote_ip' argument",
name, type);
return EINVAL;
}
+ if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) {
+ VLOG_ERR("%s: %s type requires 'remote_ip=flow' with 'local_ip=flow'",
+ name, type);
+ return EINVAL;
+ }
if (!tnl_cfg.ttl) {
tnl_cfg.ttl = DEFAULT_TTL;
}
@@ -476,10 +487,14 @@ get_tunnel_config(struct netdev_dev *dev, struct smap *args)
if (tnl_cfg->ip_dst) {
smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_dst));
+ } else if (tnl_cfg->ip_dst_flow) {
+ smap_add(args, "remote_ip", "flow");
}
if (tnl_cfg->ip_src) {
smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_src));
+ } else if (tnl_cfg->ip_src_flow) {
+ smap_add(args, "local_ip", "flow");
}
if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) {
diff --git a/lib/netdev.h b/lib/netdev.h
index aab9de0..8e87ae4 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -88,6 +88,8 @@ struct netdev_tunnel_config {
ovs_be16 dst_port;
+ bool ip_src_flow;
+ bool ip_dst_flow;
ovs_be32 ip_src;
ovs_be32 ip_dst;
diff --git a/lib/nx-match.c b/lib/nx-match.c
index bfead68..739362a 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -687,7 +687,13 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
/* Tunnel ID. */
nxm_put_64m(b, oxm ? OXM_OF_TUNNEL_ID : NXM_NX_TUN_ID,
- flow->tunnel.tun_id, match->wc.masks.tunnel.tun_id);
+ flow->tunnel.tun_id, match->wc.masks.tunnel.tun_id);
+
+ /* Other tunnel metadata. */
+ nxm_put_32m(b, NXM_NX_TUN_IPV4_SRC,
+ flow->tunnel.ip_src, match->wc.masks.tunnel.ip_src);
+ nxm_put_32m(b, NXM_NX_TUN_IPV4_DST,
+ flow->tunnel.ip_dst, match->wc.masks.tunnel.ip_dst);
/* Registers. */
for (i = 0; i < FLOW_N_REGS; i++) {
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 95d9b73..8d99844 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -111,6 +111,14 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
ds_put_format(string, " tun_id=0x%"PRIx64, ntohll(pin.fmd.tun_id));
}
+ if (pin.fmd.tun_src != htonl(0)) {
+ ds_put_format(string, " tun_src="IP_FMT, IP_ARGS(pin.fmd.tun_src));
+ }
+
+ if (pin.fmd.tun_dst != htonl(0)) {
+ ds_put_format(string, " tun_dst="IP_FMT, IP_ARGS(pin.fmd.tun_dst));
+ }
+
if (pin.fmd.metadata != htonll(0)) {
ds_put_format(string, " metadata=0x%"PRIx64, ntohll(pin.fmd.metadata));
}
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 6b78f84..0d14cc3 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -1039,16 +1039,6 @@ regs_fully_wildcarded(const struct flow_wildcards *wc)
return true;
}
-static bool
-tun_parms_fully_wildcarded(const struct flow_wildcards *wc)
-{
- return (!wc->masks.tunnel.ip_src &&
- !wc->masks.tunnel.ip_dst &&
- !wc->masks.tunnel.ip_ttl &&
- !wc->masks.tunnel.ip_tos &&
- !wc->masks.tunnel.flags);
-}
-
/* Returns a bit-mask of ofputil_protocols that can be used for sending 'match'
* to a switch (e.g. to add or remove a flow). Only NXM can handle tunnel IDs,
* registers, or fixing the Ethernet multicast bit. Otherwise, it's better to
@@ -1060,8 +1050,9 @@ ofputil_usable_protocols(const struct match *match)
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
- /* tunnel params other than tun_id can't be sent in a flow_mod */
- if (!tun_parms_fully_wildcarded(wc)) {
+ /* These tunnel params can't be sent in a flow_mod */
+ if (wc->masks.tunnel.ip_ttl
+ || wc->masks.tunnel.ip_tos || wc->masks.tunnel.flags) {
return OFPUTIL_P_NONE;
}
@@ -1107,8 +1098,10 @@ ofputil_usable_protocols(const struct match *match)
| OFPUTIL_P_OF13_OXM;
}
- /* NXM and OXM support matching tun_id. */
- if (wc->masks.tunnel.tun_id != htonll(0)) {
+ /* NXM and OXM support matching tun_id, tun_src, and tun_dst. */
+ if (wc->masks.tunnel.tun_id != htonll(0)
+ || wc->masks.tunnel.ip_src != htonl(0)
+ || wc->masks.tunnel.ip_dst != htonl(0)) {
return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
| OFPUTIL_P_OF13_OXM;
}
@@ -2450,6 +2443,8 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
pin->fmd.in_port = match->flow.in_port;
pin->fmd.tun_id = match->flow.tunnel.tun_id;
+ pin->fmd.tun_src = match->flow.tunnel.ip_src;
+ pin->fmd.tun_dst = match->flow.tunnel.ip_dst;
pin->fmd.metadata = match->flow.metadata;
memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs);
}
@@ -2550,6 +2545,12 @@ ofputil_packet_in_to_match(const struct ofputil_packet_in *pin,
if (pin->fmd.tun_id != htonll(0)) {
match_set_tun_id(match, pin->fmd.tun_id);
}
+ if (pin->fmd.tun_src != htonl(0)) {
+ match_set_tun_src(match, pin->fmd.tun_src);
+ }
+ if (pin->fmd.tun_dst != htonl(0)) {
+ match_set_tun_dst(match, pin->fmd.tun_dst);
+ }
if (pin->fmd.metadata != htonll(0)) {
match_set_metadata(match, pin->fmd.metadata);
}
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
index 8f5435a..1d7153e 100644
--- a/ofproto/tunnel.c
+++ b/ofproto/tunnel.c
@@ -44,6 +44,8 @@ struct tnl_match {
uint32_t odp_port;
uint32_t skb_mark;
bool in_key_flow;
+ bool ip_src_flow;
+ bool ip_dst_flow;
};
struct tnl_port {
@@ -91,6 +93,8 @@ tnl_port_add__(const struct ofport *ofport, uint32_t odp_port,
tnl_port->match.in_key = cfg->in_key;
tnl_port->match.ip_src = cfg->ip_src;
tnl_port->match.ip_dst = cfg->ip_dst;
+ tnl_port->match.ip_src_flow = cfg->ip_src_flow;
+ tnl_port->match.ip_dst_flow = cfg->ip_dst_flow;
tnl_port->match.skb_mark = cfg->ipsec ? IPSEC_MARK : 0;
tnl_port->match.in_key_flow = cfg->in_key_flow;
tnl_port->match.odp_port = odp_port;
@@ -236,8 +240,12 @@ tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow)
pre_flow_str = flow_to_string(flow);
}
- flow->tunnel.ip_src = tnl_port->match.ip_src;
- flow->tunnel.ip_dst = tnl_port->match.ip_dst;
+ if (!cfg->ip_src_flow) {
+ flow->tunnel.ip_src = tnl_port->match.ip_src;
+ }
+ if (!cfg->ip_dst_flow) {
+ flow->tunnel.ip_dst = tnl_port->match.ip_dst;
+ }
flow->skb_mark = tnl_port->match.skb_mark;
if (!cfg->out_key_flow) {
@@ -338,14 +346,36 @@ tnl_find(struct tnl_match *match_)
return tnl_port;
}
+ /* Flow-based remote */
+ match.ip_dst = 0;
+ match.ip_dst_flow = true;
+ tnl_port = tnl_find_exact(&match);
+ if (tnl_port) {
+ return tnl_port;
+ }
+
+ /* Flow-based everything */
+ match.ip_src = 0;
+ match.ip_src_flow = true;
+ tnl_port = tnl_find_exact(&match);
+ if (tnl_port) {
+ return tnl_port;
+ }
+
return NULL;
}
static void
tnl_match_fmt(const struct tnl_match *match, struct ds *ds)
{
- ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
- IP_ARGS(match->ip_dst));
+ if (!match->ip_dst_flow) {
+ ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
+ IP_ARGS(match->ip_dst));
+ } else if (!match->ip_src_flow) {
+ ds_put_format(ds, IP_FMT"->flow", IP_ARGS(match->ip_src));
+ } else {
+ ds_put_cstr(ds, "flow->flow");
+ }
if (match->in_key_flow) {
ds_put_cstr(ds, ", key=flow");
diff --git a/tests/ofproto.at b/tests/ofproto.at
index f41bfc3..9b1451f 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1475,6 +1475,33 @@ OFPT_BARRIER_REPLY (OF1.2):
OVS_VSWITCHD_STOP
AT_CLEANUP
+dnl This test checks that tunnel metadata is encoded in packet_in structures.
+AT_SETUP([ofproto - packet-out with tunnel metadata (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send a packet-out with set field actions to set some tunnel metadata, and forward to controller
+AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 none 'set_field:127.0.0.1->tun_src,set_field:0x01020304->tun_id,set_field:192.168.0.1->tun_dst, controller' '0001020304050010203040501234'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY tun_id=0x1020304 tun_src=127.0.0.1 tun_dst=192.168.0.1 (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+OFPT_BARRIER_REPLY (OF1.2):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto - flow monitoring])
AT_KEYWORDS([monitor])
OVS_VSWITCHD_START
diff --git a/tests/tunnel.at b/tests/tunnel.at
index 78e43f5..9d7cc5c 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -408,6 +408,34 @@ br0 (dummy at ovs-dummy):
br0 65534/100: (dummy)
p1 1/1: (vxlan: remote_ip=1.1.1.1)
])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+AT_SETUP([ofproto-dpif - set_field - tun_src/tun_dst/tun_id])
+OVS_VSWITCHD_START([dnl
+ add-port br0 p1 -- set Interface p1 type=gre options:key=flow \
+ options:remote_ip=1.1.1.1 ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=gre options:key=flow \
+ options:remote_ip=flow ofport_request=2 \
+ -- add-port br0 p3 -- set Interface p3 type=gre options:key=flow \
+ options:remote_ip=flow options:local_ip=flow ofport_request=3 \
+ -- add-port br0 p4 -- set Interface p4 type=gre options:key=3 \
+ options:remote_ip=flow ofport_request=4 \
+ -- add-port br0 p5 -- set Interface p5 type=gre options:key=flow \
+ options:remote_ip=5.5.5.5 ofport_request=5])
+ADD_OF_PORTS([br0], [90])
+AT_DATA([flows.txt], [dnl
+in_port=90 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4,resubmit:5
+in_port=1 actions=set_field:42->tun_id,output:1
+in_port=2 actions=set_field:3.3.3.3->tun_dst,output:2
+in_port=3 actions=set_field:1.1.1.1->tun_src,set_field:4.4.4.4->tun_dst,output:3
+in_port=4 actions=set_field:2.2.2.2->tun_dst,output:4
+in_port=5 actions=set_field:5->tun_id
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x2a,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,set(tunnel(tun_id=0x2a,src=0.0.0.0,dst=3.3.3.3,tos=0x0,ttl=64,flags(df,key))),1,set(tunnel(tun_id=0x2a,src=1.1.1.1,dst=4.4.4.4,tos=0x0,ttl=64,flags(df,key))),1,set(tunnel(tun_id=0x3,src=0.0.0.0,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,key))),1
+])
OVS_VSWITCHD_STOP
AT_CLEANUP
--
1.7.10.4
More information about the dev
mailing list