#ifdef HAVE_NETLINK
#include "netdev.h"
#include "netlink.h"
-#include "openflow-netlink.h"
+#include "openflow/openflow-netlink.h"
#endif
#include "command-line.h"
#include "compiler.h"
#include "dpif.h"
-#include "nicira-ext.h"
+#include "openflow/nicira-ext.h"
#include "ofp-print.h"
#include "ofpbuf.h"
-#include "openflow.h"
+#include "openflow/openflow.h"
#include "packets.h"
#include "random.h"
#include "socket-util.h"
usage();
case 'V':
- printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
+ printf("%s %s compiled "__DATE__" "__TIME__"\n",
+ program_name, VERSION BUILDNR);
exit(EXIT_SUCCESS);
case 'v':
" dump-flows SWITCH FLOW print matching FLOWs\n"
" dump-aggregate SWITCH print aggregate flow statistics\n"
" dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n"
+#ifdef SUPPORT_SNAT
+ " add-snat SWITCH IFACE IP add SNAT config to IFACE\n"
+ " del-snat SWITCH IFACE delete SNAT config on IFACE\n"
+#endif
" add-flow SWITCH FLOW add flow described by FLOW\n"
" add-flows SWITCH FILE add flows from FILE\n"
" mod-flows SWITCH FLOW modify actions of matching FLOWs\n"
" del-flows SWITCH [FLOW] delete matching FLOWs\n"
" monitor SWITCH print packets received from SWITCH\n"
+ " execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n"
"\nFor local datapaths, remote switches, and controllers:\n"
" probe VCONN probe whether VCONN is up\n"
" ping VCONN [N] latency of N-byte echos\n"
"where each SWITCH is an active OpenFlow connection method.\n",
program_name, program_name);
vconn_usage(true, false, false);
- printf("\nOptions:\n"
+ vlog_usage();
+ printf("\nOther options:\n"
" --strict use strict match for flow commands\n"
" -t, --timeout=SECS give up after SECS seconds\n"
- " -v, --verbose=MODULE[:FACILITY[:LEVEL]] set logging levels\n"
- " -v, --verbose set maximum verbosity level\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
exit(EXIT_SUCCESS);
struct ofpbuf *b;
request = make_openflow(sizeof *request, OFPT_VENDOR, &b);
- request->vendor_id = htonl(NX_VENDOR_ID);
+ request->vendor = htonl(NX_VENDOR_ID);
request->subtype = htonl(NXT_STATUS_REQUEST);
if (argc > 2) {
ofpbuf_put(b, argv[2], strlen(argv[2]));
}
reply = b->data;
if (reply->header.type != OFPT_VENDOR
- || reply->vendor_id != ntohl(NX_VENDOR_ID)
+ || reply->vendor != ntohl(NX_VENDOR_ID)
|| reply->subtype != ntohl(NXT_STATUS_REPLY)) {
ofp_print(stderr, b->data, b->size, 2);
ofp_fatal(0, "bad reply");
ah->type = htons(OFPAT_STRIP_VLAN);
} else if (!strcasecmp(act, "output")) {
port = str_to_int(arg);
+#ifdef SUPPORT_SNAT
+ } else if (!strcasecmp(act, "nat")) {
+ struct nx_action_snat *sa = (struct nx_action_snat *)ah;
+
+ if (len < sizeof *sa) {
+ ofp_fatal(0, "Insufficient room for SNAT action\n");
+ }
+
+ if (str_to_int(arg) > OFPP_MAX) {
+ ofp_fatal(0, "Invalid nat port: %s\n", arg);
+ }
+
+ act_len = sizeof *sa;
+ sa->type = htons(OFPAT_VENDOR);
+ sa->vendor = htonl(NX_VENDOR_ID);
+ sa->subtype = htons(NXAST_SNAT);
+ sa->port = htons(str_to_int(arg));
+#endif
} else if (!strcasecmp(act, "TABLE")) {
port = OFPP_TABLE;
} else if (!strcasecmp(act, "NORMAL")) {
{ "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto) },
{ "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src) },
{ "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst) },
+ { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type) },
+ { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code) }
};
const struct field *f;
static void
str_to_flow(char *string, struct ofp_match *match,
struct ofp_action_header *actions, size_t *actions_len,
- uint8_t *table_idx, uint16_t *priority,
+ uint8_t *table_idx, uint16_t *out_port, uint16_t *priority,
uint16_t *idle_timeout, uint16_t *hard_timeout)
{
if (table_idx) {
*table_idx = 0xff;
}
+ if (out_port) {
+ *out_port = OFPP_NONE;
+ }
if (priority) {
*priority = OFP_DEFAULT_PRIORITY;
}
if (table_idx && !strcmp(name, "table")) {
*table_idx = atoi(value);
+ } else if (out_port && !strcmp(name, "out_port")) {
+ *out_port = atoi(value);
} else if (priority && !strcmp(name, "priority")) {
*priority = atoi(value);
} else if (idle_timeout && !strcmp(name, "idle_timeout")) {
static void do_dump_flows(const struct settings *s, int argc, char *argv[])
{
struct ofp_flow_stats_request *req;
+ uint16_t out_port;
struct ofpbuf *request;
req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
- &req->table_id, NULL, NULL, NULL);
- memset(req->pad, 0, sizeof req->pad);
+ &req->table_id, &out_port, NULL, NULL, NULL);
+ memset(&req->pad, 0, sizeof req->pad);
+ req->out_port = htons(out_port);
dump_stats_transaction(argv[1], request);
}
{
struct ofp_aggregate_stats_request *req;
struct ofpbuf *request;
+ uint16_t out_port;
req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
- &req->table_id, NULL, NULL, NULL);
- memset(req->pad, 0, sizeof req->pad);
+ &req->table_id, &out_port, NULL, NULL, NULL);
+ memset(&req->pad, 0, sizeof req->pad);
+ req->out_port = htons(out_port);
dump_stats_transaction(argv[1], request);
}
+#ifdef SUPPORT_SNAT
+static void do_add_snat(const struct settings *s, int argc, char *argv[])
+{
+ struct vconn *vconn;
+ struct ofpbuf *buffer;
+ struct nx_act_config *nac;
+ size_t size;
+
+ /* Parse and send. */
+ size = sizeof *nac + sizeof nac->snat[0];
+ nac = make_openflow(size, OFPT_VENDOR, &buffer);
+
+ nac->header.vendor = htonl(NX_VENDOR_ID);
+ nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
+
+ nac->type = htons(NXAST_SNAT);
+ nac->snat[0].command = NXSC_ADD;
+ nac->snat[0].port = htons(str_to_int(argv[2]));
+ nac->snat[0].mac_timeout = htons(0);
+ str_to_ip(argv[3], &nac->snat[0].ip_addr_start);
+ str_to_ip(argv[3], &nac->snat[0].ip_addr_end);
+
+ open_vconn(argv[1], &vconn);
+ send_openflow_buffer(vconn, buffer);
+ vconn_close(vconn);
+}
+
+static void do_del_snat(const struct settings *s, int argc, char *argv[])
+{
+ struct vconn *vconn;
+ struct ofpbuf *buffer;
+ struct nx_act_config *nac;
+ size_t size;
+
+ /* Parse and send. */
+ size = sizeof *nac + sizeof nac->snat[0];
+ nac = make_openflow(size, OFPT_VENDOR, &buffer);
+
+ nac->header.vendor = htonl(NX_VENDOR_ID);
+ nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
+
+ nac->type = htons(NXAST_SNAT);
+ nac->snat[0].command = NXSC_DELETE;
+ nac->snat[0].port = htons(str_to_int(argv[2]));
+ nac->snat[0].mac_timeout = htons(0);
+
+ open_vconn(argv[1], &vconn);
+ send_openflow_buffer(vconn, buffer);
+ vconn_close(vconn);
+}
+#endif /* SUPPORT_SNAT */
+
static void do_add_flow(const struct settings *s, int argc, char *argv[])
{
struct vconn *vconn;
size = sizeof *ofm + actions_len;
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len,
- NULL, &priority, &idle_timeout, &hard_timeout);
+ NULL, NULL, &priority, &idle_timeout, &hard_timeout);
ofm->command = htons(OFPFC_ADD);
ofm->idle_timeout = htons(idle_timeout);
ofm->hard_timeout = htons(hard_timeout);
size = sizeof *ofm + actions_len;
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(line, &ofm->match, &ofm->actions[0], &actions_len,
- NULL, &priority, &idle_timeout, &hard_timeout);
+ NULL, NULL, &priority, &idle_timeout, &hard_timeout);
ofm->command = htons(OFPFC_ADD);
ofm->idle_timeout = htons(idle_timeout);
ofm->hard_timeout = htons(hard_timeout);
size = sizeof *ofm + actions_len;
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len,
- NULL, &priority, &idle_timeout, &hard_timeout);
+ NULL, NULL, &priority, &idle_timeout, &hard_timeout);
if (s->strict) {
ofm->command = htons(OFPFC_MODIFY_STRICT);
} else {
{
struct vconn *vconn;
uint16_t priority;
+ uint16_t out_port;
struct ofpbuf *buffer;
struct ofp_flow_mod *ofm;
size_t size;
size = sizeof *ofm;
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL,
- &priority, NULL, NULL);
+ &out_port, &priority, NULL, NULL);
if (s->strict) {
ofm->command = htons(OFPFC_DELETE_STRICT);
} else {
ofm->idle_timeout = htons(0);
ofm->hard_timeout = htons(0);
ofm->buffer_id = htonl(UINT32_MAX);
+ ofm->out_port = htons(out_port);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request);
open_vconn(argv[1], &vconn);
run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
- if (reply->size != request->size) {
+ if (reply->size != sizeof(struct ofp_header)) {
ofp_fatal(0, "reply does not match request");
}
ofpbuf_delete(reply);
count * message_size / (duration / 1000.0));
}
+static void
+do_execute(const struct settings *s, int argc, char *argv[])
+{
+ struct vconn *vconn;
+ struct ofpbuf *request;
+ struct nicira_header *nicira;
+ struct nx_command_reply *ncr;
+ uint32_t xid;
+ int i;
+
+ nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request);
+ xid = nicira->header.xid;
+ nicira->vendor = htonl(NX_VENDOR_ID);
+ nicira->subtype = htonl(NXT_COMMAND_REQUEST);
+ ofpbuf_put(request, argv[2], strlen(argv[2]));
+ for (i = 3; i < argc; i++) {
+ ofpbuf_put_zeros(request, 1);
+ ofpbuf_put(request, argv[i], strlen(argv[i]));
+ }
+ update_openflow_length(request);
+
+ open_vconn(argv[1], &vconn);
+ run(vconn_send_block(vconn, request), "send");
+
+ for (;;) {
+ struct ofpbuf *reply;
+ uint32_t status;
+
+ run(vconn_recv_xid(vconn, xid, &reply), "recv_xid");
+ if (reply->size < sizeof *ncr) {
+ ofp_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
+ reply->size, sizeof *ncr);
+ }
+ ncr = reply->data;
+ if (ncr->nxh.header.type != OFPT_VENDOR
+ || ncr->nxh.vendor != htonl(NX_VENDOR_ID)
+ || ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
+ ofp_fatal(0, "reply is invalid");
+ }
+
+ status = ntohl(ncr->status);
+ if (status & NXT_STATUS_STARTED) {
+ /* Wait for a second reply. */
+ continue;
+ } else if (status & NXT_STATUS_EXITED) {
+ fprintf(stderr, "process terminated normally with exit code %d",
+ status & NXT_STATUS_EXITSTATUS);
+ } else if (status & NXT_STATUS_SIGNALED) {
+ fprintf(stderr, "process terminated by signal %d",
+ status & NXT_STATUS_TERMSIG);
+ } else if (status & NXT_STATUS_ERROR) {
+ fprintf(stderr, "error executing command");
+ } else {
+ fprintf(stderr, "process terminated for unknown reason");
+ }
+ if (status & NXT_STATUS_COREDUMP) {
+ fprintf(stderr, " (core dumped)");
+ }
+ putc('\n', stderr);
+
+ fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
+ break;
+ }
+}
+
static void do_help(const struct settings *s, int argc UNUSED,
char *argv[] UNUSED)
{
{ "dump-tables", 1, 1, do_dump_tables },
{ "dump-flows", 1, 2, do_dump_flows },
{ "dump-aggregate", 1, 2, do_dump_aggregate },
+#ifdef SUPPORT_SNAT
+ { "add-snat", 3, 3, do_add_snat },
+ { "del-snat", 2, 2, do_del_snat },
+#endif
{ "add-flow", 2, 2, do_add_flow },
{ "add-flows", 2, 2, do_add_flows },
{ "mod-flows", 2, 2, do_mod_flows },
{ "probe", 1, 1, do_probe },
{ "ping", 1, 2, do_ping },
{ "benchmark", 3, 3, do_benchmark },
+ { "execute", 2, INT_MAX, do_execute },
{ NULL, 0, 0, NULL },
};