#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
#include "packets.h"
+#include "pcap-file.h"
#include "poll-loop.h"
#include "random.h"
#include "stream-ssl.h"
" dump-desc SWITCH print switch description\n"
" dump-tables SWITCH print table stats\n"
" mod-port SWITCH IFACE ACT modify port behavior\n"
+ " mod-table SWITCH MOD modify flow table behavior\n"
" get-frags SWITCH print fragment handling behavior\n"
" set-frags SWITCH FRAG_MODE set fragment handling behavior\n"
" dump-ports SWITCH [PORT] print port statistics\n"
" monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n"
" print packets received from SWITCH\n"
" snoop SWITCH snoop on SWITCH and its controller\n"
+ " add-group SWITCH GROUP add group described by GROUP\n"
+ " add-group SWITCH FILE add group from FILE\n"
+ " mod-group SWITCH GROUP modify specific group\n"
+ " del-groups SWITCH [GROUP] delete matching GROUPs\n"
+ " dump-group-features SWITCH print group features\n"
+ " dump-groups SWITCH print group description\n"
+ " dump-group-stats SWITCH [GROUP] print group statistics\n"
+ " add-meter SWITCH METER add meter described by METER\n"
+ " mod-meter SWITCH METER modify specific METER\n"
+ " del-meter SWITCH METER delete METER\n"
+ " del-meters SWITCH delete all meters\n"
+ " dump-meter SWITCH METER print METER configuration\n"
+ " dump-meters SWITCH print all meter configuration\n"
+ " meter-stats SWITCH [METER] print meter statistics\n"
+ " meter-features SWITCH print meter features\n"
"\nFor OpenFlow switches and controllers:\n"
" probe TARGET probe whether TARGET is up\n"
" ping TARGET [N] latency of N-byte echos\n"
char *error;
error = parse_ofp_flow_stats_request_str(&fsr, aggregate,
- argc > 2 ? argv[2] : "");
+ argc > 2 ? argv[2] : "",
+ &usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
- usable_protocols = ofputil_flow_stats_request_usable_protocols(&fsr);
-
protocol = open_vconn(argv[1], &vconn);
protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
*requestp = ofputil_encode_flow_stats_request(&fsr, protocol);
}
static enum ofputil_protocol
-open_vconn_for_flow_mod(const char *remote,
- const struct ofputil_flow_mod *fms, size_t n_fms,
- struct vconn **vconnp)
+open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp,
+ enum ofputil_protocol usable_protocols)
{
- enum ofputil_protocol usable_protocols;
enum ofputil_protocol cur_protocol;
char *usable_s;
int i;
- /* Figure out what flow formats will work. */
- usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
if (!(usable_protocols & allowed_protocols)) {
char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
usable_s = ofputil_protocols_to_string(usable_protocols);
static void
ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
- size_t n_fms)
+ size_t n_fms, enum ofputil_protocol usable_protocols)
{
enum ofputil_protocol protocol;
struct vconn *vconn;
size_t i;
- protocol = open_vconn_for_flow_mod(remote, fms, n_fms, &vconn);
+ protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
for (i = 0; i < n_fms; i++) {
struct ofputil_flow_mod *fm = &fms[i];
static void
ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
{
+ enum ofputil_protocol usable_protocols;
struct ofputil_flow_mod *fms = NULL;
size_t n_fms = 0;
char *error;
- error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+ error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
+ &usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
- ofctl_flow_mod__(argv[1], fms, n_fms);
+ ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
free(fms);
}
} else {
struct ofputil_flow_mod fm;
char *error;
+ enum ofputil_protocol usable_protocols;
- error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command);
+ error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
+ &usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
- ofctl_flow_mod__(argv[1], &fm, 1);
+ ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
}
}
run(retval, "vconn_recv");
if (timestamp) {
- char *s = xastrftime("%Y-%m-%d %H:%M:%S: ", time_wall(), true);
+ char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.###: ",
+ time_wall_msec(), true);
fputs(s, stderr);
free(s);
}
{
struct vconn *vconn;
int i;
+ enum ofputil_protocol usable_protocols;
open_vconn(argv[1], &vconn);
for (i = 2; i < argc; i++) {
struct ofpbuf *msg;
char *error;
- error = parse_flow_monitor_request(&fmr, arg + 6);
+ error = parse_flow_monitor_request(&fmr, arg + 6,
+ &usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
struct vconn *vconn;
char *error;
int i;
+ enum ofputil_protocol usable_protocols; /* TODO: Use in proto selection */
ofpbuf_init(&ofpacts, 64);
- error = parse_ofpacts(argv[3], &ofpacts);
+ error = parse_ofpacts(argv[3], &ofpacts, &usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
vconn_close(vconn);
}
+static void
+ofctl_mod_table(int argc OVS_UNUSED, char *argv[])
+{
+ enum ofputil_protocol protocol, usable_protocols;
+ struct ofputil_table_mod tm;
+ struct vconn *vconn;
+ char *error;
+ int i;
+
+ error = parse_ofp_table_mod(&tm, argv[2], argv[3], &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+
+ protocol = open_vconn(argv[1], &vconn);
+ if (!(protocol & usable_protocols)) {
+ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+ enum ofputil_protocol f = 1 << i;
+ if (f != protocol
+ && f & usable_protocols
+ && try_set_protocol(vconn, f, &protocol)) {
+ protocol = f;
+ break;
+ }
+ }
+ }
+
+ if (!(protocol & usable_protocols)) {
+ char *usable_s = ofputil_protocols_to_string(usable_protocols);
+ ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s);
+ }
+
+ transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
+ vconn_close(vconn);
+}
+
static void
ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
{
count * message_size / (duration / 1000.0));
}
+static void
+ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms,
+ size_t n_gms)
+{
+ struct ofputil_group_mod *gm;
+ struct ofpbuf *request;
+
+ struct vconn *vconn;
+ size_t i;
+
+ open_vconn(remote, &vconn);
+
+ for (i = 0; i < n_gms; i++) {
+ gm = &gms[i];
+ request = ofputil_encode_group_mod(vconn_get_version(vconn), gm);
+ if (request) {
+ transact_noreply(vconn, request);
+ }
+ }
+
+ vconn_close(vconn);
+
+}
+
+
+static void
+ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+{
+ struct ofputil_group_mod *gms = NULL;
+ enum ofputil_protocol usable_protocols;
+ size_t n_gms = 0;
+ char *error;
+
+ error = parse_ofp_group_mod_file(argv[2], command, &gms, &n_gms,
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ ofctl_group_mod__(argv[1], gms, n_gms);
+ free(gms);
+}
+
+static void
+ofctl_group_mod(int argc, char *argv[], uint16_t command)
+{
+ if (argc > 2 && !strcmp(argv[2], "-")) {
+ ofctl_group_mod_file(argc, argv, command);
+ } else {
+ enum ofputil_protocol usable_protocols;
+ struct ofputil_group_mod gm;
+ char *error;
+
+ error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2] : "",
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ ofctl_group_mod__(argv[1], &gm, 1);
+ }
+}
+
+static void
+ofctl_add_group(int argc, char *argv[])
+{
+ ofctl_group_mod(argc, argv, OFPGC11_ADD);
+}
+
+static void
+ofctl_add_groups(int argc, char *argv[])
+{
+ ofctl_group_mod_file(argc, argv, OFPGC11_ADD);
+}
+
+static void
+ofctl_mod_group(int argc, char *argv[])
+{
+ ofctl_group_mod(argc, argv, OFPGC11_MODIFY);
+}
+
+static void
+ofctl_del_groups(int argc, char *argv[])
+{
+ ofctl_group_mod(argc, argv, OFPGC11_DELETE);
+}
+
+static void
+ofctl_dump_group_stats(int argc, char *argv[])
+{
+ enum ofputil_protocol usable_protocols;
+ struct ofputil_group_mod gm;
+ struct ofpbuf *request;
+ struct vconn *vconn;
+ uint32_t group_id;
+ char *error;
+
+ memset(&gm, 0, sizeof gm);
+
+ error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
+ argc > 2 ? argv[2] : "",
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+
+ group_id = gm.group_id;
+
+ open_vconn(argv[1], &vconn);
+ request = ofputil_encode_group_stats_request(vconn_get_version(vconn),
+ group_id);
+ if (request) {
+ dump_stats_transaction(vconn, request);
+ }
+
+ vconn_close(vconn);
+}
+
+static void
+ofctl_dump_group_desc(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+
+ open_vconn(argv[1], &vconn);
+
+ request = ofputil_encode_group_desc_request(vconn_get_version(vconn));
+ if (request) {
+ dump_stats_transaction(vconn, request);
+ }
+
+ vconn_close(vconn);
+}
+
+static void
+ofctl_dump_group_features(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+
+ open_vconn(argv[1], &vconn);
+ request = ofputil_encode_group_features_request(vconn_get_version(vconn));
+ if (request) {
+ dump_stats_transaction(vconn, request);
+ }
+
+ vconn_close(vconn);
+}
+
static void
ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
struct cls_cursor cursor;
struct fte *fte, *next;
+ ovs_rwlock_wrlock(&cls->rwlock);
cls_cursor_init(&cursor, cls, NULL);
CLS_CURSOR_FOR_EACH_SAFE (fte, next, rule, &cursor) {
classifier_remove(cls, &fte->rule);
fte_free(fte);
}
+ ovs_rwlock_unlock(&cls->rwlock);
classifier_destroy(cls);
}
cls_rule_init(&fte->rule, match, priority);
fte->versions[index] = version;
+ ovs_rwlock_wrlock(&cls->rwlock);
old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
+ ovs_rwlock_unlock(&cls->rwlock);
if (old) {
fte_version_free(old->versions[index]);
fte->versions[!index] = old->versions[!index];
struct fte_version *version;
struct ofputil_flow_mod fm;
char *error;
+ enum ofputil_protocol usable;
- error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s));
+ error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable);
if (error) {
ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
}
+ usable_protocols &= usable;
version = xmalloc(sizeof *version);
version->cookie = fm.new_cookie;
version->idle_timeout = fm.idle_timeout;
version->hard_timeout = fm.hard_timeout;
- version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF10_EMERG);
+ version->flags = fm.flags & (OFPUTIL_FF_SEND_FLOW_REM
+ | OFPUTIL_FF_EMERG);
version->ofpacts = fm.ofpacts;
version->ofpacts_len = fm.ofpacts_len;
- usable_protocols &= ofputil_usable_protocols(&fm.match);
-
fte_insert(cls, &fm.match, fm.priority, version, index);
}
ds_destroy(&s);
list_init(&requests);
/* Delete flows that exist on the switch but not in the file. */
+ ovs_rwlock_rdlock(&cls.rwlock);
cls_cursor_init(&cursor, &cls, NULL);
CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
struct fte_version *file_ver = fte->versions[FILE_IDX];
fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
}
}
+ ovs_rwlock_unlock(&cls.rwlock);
transact_multiple_noreply(vconn, &requests);
vconn_close(vconn);
ds_init(&a_s);
ds_init(&b_s);
+ ovs_rwlock_rdlock(&cls.rwlock);
cls_cursor_init(&cursor, &cls, NULL);
CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
struct fte_version *a = fte->versions[0];
}
}
}
+ ovs_rwlock_unlock(&cls.rwlock);
ds_destroy(&a_s);
ds_destroy(&b_s);
exit(2);
}
}
+
+static void
+ofctl_meter_mod__(const char *bridge, const char *str, int command)
+{
+ struct ofputil_meter_mod mm;
+ struct vconn *vconn;
+ enum ofputil_protocol protocol;
+ enum ofputil_protocol usable_protocols;
+ enum ofp_version version;
+
+ if (str) {
+ char *error;
+ error = parse_ofp_meter_mod_str(&mm, str, command, &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ } else {
+ usable_protocols = OFPUTIL_P_OF13_UP;
+ mm.command = command;
+ mm.meter.meter_id = OFPM13_ALL;
+ }
+
+ protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols);
+ version = ofputil_protocol_to_ofp_version(protocol);
+ transact_noreply(vconn, ofputil_encode_meter_mod(version, &mm));
+ vconn_close(vconn);
+}
+
+static void
+ofctl_meter_request__(const char *bridge, const char *str,
+ enum ofputil_meter_request_type type)
+{
+ struct ofputil_meter_mod mm;
+ struct vconn *vconn;
+ enum ofputil_protocol usable_protocols;
+ enum ofputil_protocol protocol;
+ enum ofp_version version;
+
+ if (str) {
+ char *error;
+ error = parse_ofp_meter_mod_str(&mm, str, -1, &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ } else {
+ usable_protocols = OFPUTIL_P_OF13_UP;
+ mm.meter.meter_id = OFPM13_ALL;
+ }
+
+ protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols);
+ version = ofputil_protocol_to_ofp_version(protocol);
+ transact_noreply(vconn, ofputil_encode_meter_request(version,
+ type,
+ mm.meter.meter_id));
+ vconn_close(vconn);
+}
+
+
+static void
+ofctl_add_meter(int argc OVS_UNUSED, char *argv[])
+{
+ ofctl_meter_mod__(argv[1], argv[2], OFPMC13_ADD);
+}
+
+static void
+ofctl_mod_meter(int argc OVS_UNUSED, char *argv[])
+{
+ ofctl_meter_mod__(argv[1], argv[2], OFPMC13_MODIFY);
+}
+
+static void
+ofctl_del_meters(int argc, char *argv[])
+{
+ ofctl_meter_mod__(argv[1], argc > 2 ? argv[2] : NULL, OFPMC13_DELETE);
+}
+
+static void
+ofctl_dump_meters(int argc, char *argv[])
+{
+ ofctl_meter_request__(argv[1], argc > 2 ? argv[2] : NULL,
+ OFPUTIL_METER_CONFIG);
+}
+
+static void
+ofctl_meter_stats(int argc, char *argv[])
+{
+ ofctl_meter_request__(argv[1], argc > 2 ? argv[2] : NULL,
+ OFPUTIL_METER_STATS);
+}
+
+static void
+ofctl_meter_features(int argc OVS_UNUSED, char *argv[])
+{
+ ofctl_meter_request__(argv[1], NULL, OFPUTIL_METER_FEATURES);
+}
+
\f
/* Undocumented commands for unit testing. */
static void
-ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
+ enum ofputil_protocol usable_protocols)
{
- enum ofputil_protocol usable_protocols;
enum ofputil_protocol protocol = 0;
char *usable_s;
size_t i;
- usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
usable_s = ofputil_protocols_to_string(usable_protocols);
printf("usable protocols: %s\n", usable_s);
free(usable_s);
static void
ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
{
+ enum ofputil_protocol usable_protocols;
struct ofputil_flow_mod fm;
char *error;
- error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD);
+ error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, &usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
- ofctl_parse_flows__(&fm, 1);
+ ofctl_parse_flows__(&fm, 1, usable_protocols);
}
/* "parse-flows FILENAME": reads the named file as a sequence of flows (like
static void
ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
{
+ enum ofputil_protocol usable_protocols;
struct ofputil_flow_mod *fms = NULL;
size_t n_fms = 0;
char *error;
- error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+ error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms,
+ &usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
- ofctl_parse_flows__(fms, n_fms);
+ ofctl_parse_flows__(fms, n_fms, usable_protocols);
free(fms);
}
ds_destroy(&in);
}
+/* "parse-pcap PCAP": read packets from PCAP and print their flows. */
+static void
+ofctl_parse_pcap(int argc OVS_UNUSED, char *argv[])
+{
+ FILE *pcap;
+
+ pcap = pcap_open(argv[1], "rb");
+ if (!pcap) {
+ ovs_fatal(errno, "%s: open failed", argv[1]);
+ }
+
+ for (;;) {
+ struct ofpbuf *packet;
+ struct flow flow;
+ int error;
+
+ error = pcap_read(pcap, &packet);
+ if (error == EOF) {
+ break;
+ } else if (error) {
+ ovs_fatal(error, "%s: read failed", argv[1]);
+ }
+
+ flow_extract(packet, 0, 0, NULL, NULL, &flow);
+ flow_print(stdout, &flow);
+ putchar('\n');
+ ofpbuf_delete(packet);
+ }
+}
+
/* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and
* mask values to and from various formats and prints the results. */
static void
enum ofperr error;
char *error_s;
+ enum ofputil_protocol usable_protocols; /* Unused for now. */
+
match_init_catchall(&match);
match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
match.wc.masks.vlan_tci = htons(strtoul(argv[2], NULL, 16));
string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
printf("%s -> ", string_s);
fflush(stdout);
- error_s = parse_ofp_str(&fm, -1, string_s);
+ error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols);
if (error_s) {
ovs_fatal(0, "%s", error_s);
}
{ "del-flows", 1, 2, ofctl_del_flows },
{ "replace-flows", 2, 2, ofctl_replace_flows },
{ "diff-flows", 2, 2, ofctl_diff_flows },
+ { "add-meter", 2, 2, ofctl_add_meter },
+ { "mod-meter", 2, 2, ofctl_mod_meter },
+ { "del-meter", 2, 2, ofctl_del_meters },
+ { "del-meters", 1, 1, ofctl_del_meters },
+ { "dump-meter", 2, 2, ofctl_dump_meters },
+ { "dump-meters", 1, 1, ofctl_dump_meters },
+ { "meter-stats", 1, 2, ofctl_meter_stats },
+ { "meter-features", 1, 1, ofctl_meter_features },
{ "packet-out", 4, INT_MAX, ofctl_packet_out },
{ "dump-ports", 1, 2, ofctl_dump_ports },
{ "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
{ "mod-port", 3, 3, ofctl_mod_port },
+ { "mod-table", 3, 3, ofctl_mod_table },
{ "get-frags", 1, 1, ofctl_get_frags },
{ "set-frags", 2, 2, ofctl_set_frags },
{ "ofp-parse", 1, 1, ofctl_ofp_parse },
{ "probe", 1, 1, ofctl_probe },
{ "ping", 1, 2, ofctl_ping },
{ "benchmark", 3, 3, ofctl_benchmark },
+
+ { "add-group", 1, 2, ofctl_add_group },
+ { "add-groups", 1, 2, ofctl_add_groups },
+ { "mod-group", 1, 2, ofctl_mod_group },
+ { "del-groups", 1, 2, ofctl_del_groups },
+ { "dump-groups", 1, 1, ofctl_dump_group_desc },
+ { "dump-group-stats", 1, 2, ofctl_dump_group_stats },
+ { "dump-group-features", 1, 1, ofctl_dump_group_features },
{ "help", 0, INT_MAX, ofctl_help },
/* Undocumented commands for testing. */
{ "parse-ofp11-match", 0, 0, ofctl_parse_ofp11_match },
{ "parse-ofp11-actions", 0, 0, ofctl_parse_ofp11_actions },
{ "parse-ofp11-instructions", 0, 0, ofctl_parse_ofp11_instructions },
+ { "parse-pcap", 1, 1, ofctl_parse_pcap },
{ "check-vlan", 2, 2, ofctl_check_vlan },
{ "print-error", 1, 1, ofctl_print_error },
{ "encode-error-reply", 2, 2, ofctl_encode_error_reply },