- Added support for querying, modifying, and deleting flows
based on flow cookie when using NXM.
- Added new NXM_PACKET_IN format.
+ - Added new NXAST_DEC_TTL action.
- ovs-ofctl:
- Added daemonization support to the monitor and snoop commands.
- ovs-vsctl:
NXAST_RESUBMIT_TABLE, /* struct nx_action_resubmit */
NXAST_OUTPUT_REG, /* struct nx_action_output_reg */
NXAST_LEARN, /* struct nx_action_learn */
- NXAST_EXIT /* struct nx_action_header */
+ NXAST_EXIT, /* struct nx_action_header */
+ NXAST_DEC_TTL, /* struct nx_action_header */
};
/* Header for Nicira-defined actions. */
OFPC_FRAG_DROP = 1, /* Drop fragments. */
OFPC_FRAG_REASM = 2, /* Reassemble (only if OFPC_IP_REASM set). */
OFPC_FRAG_NX_MATCH = 3, /* Make first fragments available for matching. */
- OFPC_FRAG_MASK = 3
+ OFPC_FRAG_MASK = 3,
+
+ /* TTL processing - applicable for IP and MPLS packets. */
+ OFPC_INVALID_TTL_TO_CONTROLLER = 1 << 2, /* Send packets with invalid TTL
+ to the controller. */
};
/* Switch configuration. */
/* Why is this packet being sent to the controller? */
enum ofp_packet_in_reason {
OFPR_NO_MATCH, /* No matching flow. */
- OFPR_ACTION /* Action explicitly output to controller. */
+ OFPR_ACTION, /* Action explicitly output to controller. */
+ OFPR_INVALID_TTL /* Packet has invalid TTL. */
};
/* Packet received on port (datapath -> controller). */
case OFPUTIL_NXAST_EXIT:
ofputil_put_NXAST_EXIT(b);
break;
+
+ case OFPUTIL_NXAST_DEC_TTL:
+ ofputil_put_NXAST_DEC_TTL(b);
+ break;
}
}
}
}
- if (pin.reason == OFPR_ACTION) {
+ switch (pin.reason) {
+ case OFPR_NO_MATCH:
+ ds_put_cstr(string, " (via no_match)");
+ break;
+ case OFPR_ACTION:
ds_put_cstr(string, " (via action)");
- } else if (pin.reason != OFPR_NO_MATCH) {
+ break;
+ case OFPR_INVALID_TTL:
+ ds_put_cstr(string, " (via invalid_ttl)");
+ break;
+ default:
ds_put_format(string, " (***reason %"PRIu8"***)", pin.reason);
+ break;
}
ds_put_format(string, " data_len=%zu", pin.packet_len);
learn_format((const struct nx_action_learn *) a, s);
break;
+ case OFPUTIL_NXAST_DEC_TTL:
+ ds_put_cstr(s, "dec_ttl");
+ break;
+
case OFPUTIL_NXAST_EXIT:
ds_put_cstr(s, "exit");
break;
static void
ofp_print_switch_config(struct ds *string, const struct ofp_switch_config *osc)
{
- uint16_t flags;
+ enum ofp_config_flags flags;
flags = ntohs(osc->flags);
ds_put_format(string, " frags=%s", ofputil_frag_handling_to_string(flags));
flags &= ~OFPC_FRAG_MASK;
+ if (flags & OFPC_INVALID_TTL_TO_CONTROLLER) {
+ ds_put_format(string, " invalid_ttl_to_controller");
+ flags &= ~OFPC_INVALID_TTL_TO_CONTROLLER;
+ }
+
if (flags) {
ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
}
case OFPUTIL_NXAST_NOTE:
case OFPUTIL_NXAST_SET_TUNNEL64:
case OFPUTIL_NXAST_EXIT:
+ case OFPUTIL_NXAST_DEC_TTL:
break;
}
NXAST_ACTION(NXAST_OUTPUT_REG, nx_action_output_reg, 0, NULL)
NXAST_ACTION(NXAST_LEARN, nx_action_learn, 1, "learn")
NXAST_ACTION(NXAST_EXIT, nx_action_header, 0, "exit")
+NXAST_ACTION(NXAST_DEC_TTL, nx_action_header, 0, "dec_ttl")
#undef NXAST_ACTION
const void *packet;
size_t packet_len;
- uint8_t reason; /* One of OFPR_*. */
+ enum ofp_packet_in_reason reason; /* One of OFPRR_*. */
uint8_t table_id;
ovs_be64 cookie;
/* type == OFCONN_PRIMARY only. */
enum nx_role role; /* Role. */
+ bool invalid_ttl_to_controller; /* Send packets with invalid TTL
+ to the controller. */
struct hmap_node hmap_node; /* In struct connmgr's "controllers" map. */
enum ofproto_band band; /* In-band or out-of-band? */
};
ofconn->role = role;
}
+void
+ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool val)
+{
+ ofconn->invalid_ttl_to_controller = val;
+}
+
+bool
+ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn)
+{
+ return ofconn->invalid_ttl_to_controller;
+}
+
/* Returns the currently configured flow format for 'ofconn', one of NXFF_*.
*
* The default, if no other format has been set, is NXFF_OPENFLOW10. */
ofconn->pktbuf = NULL;
ofconn->miss_send_len = 0;
ofconn->reply_counter = rconn_packet_counter_create ();
+ ofconn->invalid_ttl_to_controller = false;
return ofconn;
}
/* Returns true if 'ofconn' should receive asynchronous messages. */
static bool
-ofconn_receives_async_msgs(const struct ofconn *ofconn)
+ofconn_receives_async_msgs__(const struct ofconn *ofconn)
{
- if (!rconn_is_connected(ofconn->rconn)) {
- return false;
- } else if (ofconn->type == OFCONN_PRIMARY) {
+ if (ofconn->type == OFCONN_PRIMARY) {
/* Primary controllers always get asynchronous messages unless they
* have configured themselves as "slaves". */
return ofconn->role != NX_ROLE_SLAVE;
}
}
+static bool
+ofconn_receives_async_msgs(const struct ofconn *ofconn)
+{
+ if (!rconn_is_connected(ofconn->rconn)) {
+ return false;
+ } else {
+ return ofconn_receives_async_msgs__(ofconn);
+ }
+}
+
+static bool
+ofconn_interested_in_packet(const struct ofconn *ofconn,
+ const struct ofputil_packet_in *pin)
+{
+ if (!rconn_is_connected(ofconn->rconn)) {
+ return false;
+ } else if (pin->reason == OFPR_INVALID_TTL) {
+ return ofconn->invalid_ttl_to_controller;
+ } else {
+ return ofconn_receives_async_msgs__(ofconn);
+ }
+}
+
/* Returns a human-readable name for an OpenFlow connection between 'mgr' and
* 'target', suitable for use in log messages for identifying the connection.
*
struct ofconn *ofconn;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- if (ofconn_receives_async_msgs(ofconn)) {
+ if (ofconn_interested_in_packet(ofconn, pin)) {
schedule_packet_in(ofconn, *pin, flow);
}
}
bool ofconn_get_flow_mod_table_id(const struct ofconn *);
void ofconn_set_flow_mod_table_id(struct ofconn *, bool enable);
+void ofconn_set_invalid_ttl_to_controller(struct ofconn *, bool);
+bool ofconn_get_invalid_ttl_to_controller(struct ofconn *);
+
int ofconn_get_miss_send_len(const struct ofconn *);
void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len);
}
static void
-execute_controller_action(struct action_xlate_ctx *ctx, int len)
+execute_controller_action(struct action_xlate_ctx *ctx, int len,
+ enum ofp_packet_in_reason reason)
{
struct ofputil_packet_in pin;
struct ofpbuf *packet;
pin.packet = packet->data;
pin.packet_len = packet->size;
- pin.reason = OFPR_ACTION;
+ pin.reason = reason;
pin.table_id = ctx->table_id;
pin.cookie = ctx->cookie;
ofpbuf_delete(packet);
}
+static bool
+compose_dec_ttl(struct action_xlate_ctx *ctx)
+{
+ if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
+ ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
+ return false;
+ }
+
+ if (ctx->flow.nw_ttl > 1) {
+ ctx->flow.nw_ttl--;
+ return false;
+ } else {
+ execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL);
+
+ /* Stop processing for current table. */
+ return true;
+ }
+}
+
static void
xlate_output_action__(struct action_xlate_ctx *ctx,
uint16_t port, uint16_t max_len)
flood_packets(ctx, true);
break;
case OFPP_CONTROLLER:
- execute_controller_action(ctx, max_len);
+ execute_controller_action(ctx, max_len, OFPR_ACTION);
break;
case OFPP_LOCAL:
compose_output_action(ctx, OFPP_LOCAL);
}
break;
+ case OFPUTIL_NXAST_DEC_TTL:
+ if (compose_dec_ttl(ctx)) {
+ goto out;
+ }
+ break;
+
case OFPUTIL_NXAST_EXIT:
ctx->exit = true;
break;
}
}
+out:
/* We've let OFPP_NORMAL and the learning action look at the packet,
* so drop it now if forwarding is disabled. */
if (port && !stp_forward_in_state(port->stp_state)) {
case OFPC_FRAG_NX_MATCH:
/* Nothing to do. */
break;
+
+ case OFPC_INVALID_TTL_TO_CONTROLLER:
+ NOT_REACHED();
}
}
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofp_switch_config *osc;
+ enum ofp_config_flags flags;
struct ofpbuf *buf;
/* Send reply. */
osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
- osc->flags = htons(ofproto->frag_handling);
+ flags = ofproto->frag_handling;
+ if (ofconn_get_invalid_ttl_to_controller(ofconn)) {
+ flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
+ }
+ osc->flags = htons(flags);
osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
ofconn_send_reply(ofconn, buf);
}
}
}
+ ofconn_set_invalid_ttl_to_controller(ofconn,
+ (flags & OFPC_INVALID_TTL_TO_CONTROLLER));
ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \
50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
"], [0], [dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 data_len=60 buffer=0x00000111
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800 proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
])
AT_CLEANUP
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - dec_ttl])
+OVS_VSWITCHD_START
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1 action=dec_ttl,output:2,resubmit(1,1),output:4
+table=1 in_port=1 action=dec_ttl,output:3
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),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=2,frag=no)' -generate], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+ [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=1,frag=no)),2,4
+This flow is not cachable.
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),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=3,frag=no)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=2,frag=no)),2,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=1,frag=no)),3,4
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=127,frag=no)),2,set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=126,frag=no)),3,4
+])
+
+AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),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=2,frag=no)' -generate], [0], [stdout])
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): table_id=1 total_len=42 in_port=1 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 (via invalid_ttl) data_len=42 (unbuffered)
+priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+
AT_SETUP([ofproto-dpif - output, OFPP_NONE ingress port])
OVS_VSWITCHD_START(
[add-port br0 p1 -- set Interface p1 type=dummy --\
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 data_len=60 (unbuffered)
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 data_len=60 (unbuffered)
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 data_len=60 (unbuffered)
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
])
the configured controller is disconnected, no traffic is sent, so
monitoring will not show any traffic.
.
-.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR]"
+.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fIinvalid_ttl\fR]"
Connects to \fIswitch\fR and prints to the console all OpenFlow
messages received. Usually, \fIswitch\fR should specify the name of a
bridge in the \fBovs\-vswitchd\fR database.
specified on this argument. (Thus, if \fImiss\-len\fR is not
specified, very little traffic will ordinarily be printed.)
.IP
+.IP
+If \fBinvalid_ttl\fR is passed, \fBovs\-ofctl\fR sends an OpenFlow ``set
+configuration'' message at connection setup time that requests
+\fIINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can
+receive ``packets-in'' messages when TTL reaches zero on \fBdec_ttl\fR action.
+.IP
+
This command may be useful for debugging switch or controller
implementations.
.
Restores the queue to the value it was before any \fBset_queue\fR
actions were applied.
.
+.IP \fBdec_ttl\fR
+Decrement TTL of IPv4 packet or hop limit of IPv6 packet. If the
+TTL or hop limit is initially zero, no decrement occurs. Instead,
+a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR is
+sent to each connected controller that has enabled receiving them,
+if any. Processing the current set of actions then stops.
+However, if the current set of actions was reached through
+``resubmit'' then remaining actions in outer levels resume
+processing.
+.
.IP \fBnote:\fR[\fIhh\fR]...
Does nothing at all. Any number of bytes represented as hex digits
\fIhh\fR may be included. Pairs of hex digits may be separated by
ofputil_packet_in_format_to_string(packet_in_format));
}
+static int
+monitor_set_invalid_ttl_to_controller(struct vconn *vconn)
+{
+ struct ofp_switch_config config;
+ enum ofp_config_flags flags;
+
+ fetch_switch_config(vconn, &config);
+ flags = ntohs(config.flags);
+ if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
+ /* Set the invalid ttl config. */
+ flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
+
+ config.flags = htons(flags);
+ set_switch_config(vconn, &config);
+
+ /* Then retrieve the configuration to see if it really took. OpenFlow
+ * doesn't define error reporting for bad modes, so this is all we can
+ * do. */
+ fetch_switch_config(vconn, &config);
+ flags = ntohs(config.flags);
+ if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
+ ovs_fatal(0, "setting invalid_ttl_to_controller failed (this "
+ "switch probably doesn't support mode)");
+ return -EOPNOTSUPP;
+ }
+ }
+ return 0;
+}
+
static void
monitor_vconn(struct vconn *vconn)
{
config.miss_send_len = htons(atoi(argv[2]));
set_switch_config(vconn, &config);
}
+ if (argc > 3) {
+ if (!strcmp(argv[3], "invalid_ttl")) {
+ monitor_set_invalid_ttl_to_controller(vconn);
+ }
+ }
monitor_vconn(vconn);
}
static const struct command all_commands[] = {
{ "show", 1, 1, do_show },
- { "monitor", 1, 2, do_monitor },
+ { "monitor", 1, 3, do_monitor },
{ "snoop", 1, 1, do_snoop },
{ "dump-desc", 1, 1, do_dump_desc },
{ "dump-tables", 1, 1, do_dump_tables },