*/
#include <config.h>
+#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <netinet/in.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "socket-util.h"
#include "openflow.h"
#include "ofp-print.h"
+#include "packets.h"
#include "random.h"
-#include "signal.h"
+#include "timeval.h"
#include "vconn.h"
#include "vconn-ssl.h"
#include "vlog.h"
#define THIS_MODULE VLM_dpctl
-#define DEFAULT_MAX_IDLE 60
+#define DEFAULT_IDLE_TIMEOUT 60
#define MAX_ADD_ACTS 5
static const char* ifconfigbin = "/sbin/ifconfig";
+#define MOD_PORT_CMD_UP "up"
+#define MOD_PORT_CMD_DOWN "down"
+#define MOD_PORT_CMD_FLOOD "flood"
+#define MOD_PORT_CMD_NOFLOOD "noflood"
+
struct command {
const char *name;
int min_args;
struct command *p;
set_program_name(argv[0]);
+ time_init();
vlog_init();
parse_options(argc, argv);
+ signal(SIGPIPE, SIG_IGN);
argc -= optind;
argv += optind;
for (;;) {
unsigned long int timeout;
- int indexptr;
int c;
- c = getopt_long(argc, argv, short_options, long_options, &indexptr);
+ c = getopt_long(argc, argv, short_options, long_options, NULL);
if (c == -1) {
break;
}
if (timeout <= 0) {
fatal(0, "value %s on -t or --timeout is not at least 1",
optarg);
- } else if (timeout < UINT_MAX) {
- /* Add 1 because historical implementations allow an alarm to
- * occur up to a second early. */
- alarm(timeout + 1);
} else {
- alarm(UINT_MAX);
+ time_alarm(timeout);
}
- signal(SIGALRM, SIG_DFL);
break;
case 'h':
" monitor nl:DP_ID print packets received\n"
#endif
"\nFor local datapaths and remote switches:\n"
- " show SWITCH show information\n"
+ " show SWITCH show basic information\n"
+ " status SWITCH [KEY] report statistics (about KEY)\n"
+ " dump-version SWITCH print version information\n"
" dump-tables SWITCH print table stats\n"
+ " mod-port SWITCH IFACE ACT modify port behavior\n"
" dump-ports SWITCH print port statistics\n"
" dump-flows SWITCH print all flow entries\n"
" dump-flows SWITCH FLOW print matching FLOWs\n"
dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
}
+static void
+do_status(int argc, char *argv[])
+{
+ struct buffer *request;
+ alloc_stats_request(0, OFPST_SWITCH, &request);
+ if (argc > 2) {
+ buffer_put(request, argv[2], strlen(argv[2]));
+ }
+ dump_stats_transaction(argv[1], request);
+}
+
+static void
+do_dump_version(int argc, char *argv[])
+{
+ dump_trivial_stats_transaction(argv[1], OFPST_VERSION);
+}
static void
do_dump_tables(int argc, char *argv[])
static uint32_t
str_to_int(const char *str)
{
+ char *tail;
uint32_t value;
- if (sscanf(str, "%"SCNu32, &value) != 1) {
+
+ errno = 0;
+ value = strtoul(str, &tail, 0);
+ if (errno == EINVAL || errno == ERANGE || *tail) {
fatal(0, "invalid numeric format %s", str);
}
return value;
}
}
-static void
-str_to_ip(const char *str, uint32_t *ip)
+static uint32_t
+str_to_ip(const char *str_, uint32_t *ip)
{
+ char *str = xstrdup(str_);
+ char *save_ptr = NULL;
+ const char *name, *netmask;
struct in_addr in_addr;
- int retval;
+ int n_wild, retval;
- retval = lookup_ip(str, &in_addr);
+ name = strtok_r(str, "//", &save_ptr);
+ retval = name ? lookup_ip(name, &in_addr) : EINVAL;
if (retval) {
fatal(0, "%s: could not convert to IP address", str);
}
*ip = in_addr.s_addr;
+
+ netmask = strtok_r(NULL, "//", &save_ptr);
+ if (netmask) {
+ uint8_t o[4];
+ if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
+ &o[0], &o[1], &o[2], &o[3]) == 4) {
+ uint32_t nm = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3];
+ int i;
+
+ /* Find first 1-bit. */
+ for (i = 0; i < 32; i++) {
+ if (nm & (1u << i)) {
+ break;
+ }
+ }
+ n_wild = i;
+
+ /* Verify that the rest of the bits are 1-bits. */
+ for (; i < 32; i++) {
+ if (!(nm & (1u << i))) {
+ fatal(0, "%s: %s is not a valid netmask", str, netmask);
+ }
+ }
+ } else {
+ int prefix = atoi(netmask);
+ if (prefix <= 0 || prefix > 32) {
+ fatal(0, "%s: network prefix bits not between 1 and 32", str);
+ }
+ n_wild = 32 - prefix;
+ }
+ } else {
+ n_wild = 0;
+ }
+
+ free(str);
+ return n_wild;
}
static void
*n_actions = i;
}
-static void
-str_to_flow(char *string, struct ofp_match *match,
- struct ofp_action *action, int *n_actions, uint8_t *table_idx,
- uint16_t *priority, uint16_t *max_idle)
-{
- struct field {
- const char *name;
- uint32_t wildcard;
- enum { F_U8, F_U16, F_MAC, F_IP } type;
- size_t offset;
+struct protocol {
+ const char *name;
+ uint16_t dl_type;
+ uint8_t nw_proto;
+};
+
+static bool
+parse_protocol(const char *name, const struct protocol **p_out)
+{
+ static const struct protocol protocols[] = {
+ { "ip", ETH_TYPE_IP },
+ { "arp", ETH_TYPE_ARP },
+ { "icmp", ETH_TYPE_IP, IP_TYPE_ICMP },
+ { "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
+ { "udp", ETH_TYPE_IP, IP_TYPE_UDP },
};
+ const struct protocol *p;
+
+ for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
+ if (!strcmp(p->name, name)) {
+ *p_out = p;
+ return true;
+ }
+ }
+ *p_out = NULL;
+ return false;
+}
+
+struct field {
+ const char *name;
+ uint32_t wildcard;
+ enum { F_U8, F_U16, F_MAC, F_IP } type;
+ size_t offset, shift;
+};
+static bool
+parse_field(const char *name, const struct field **f_out)
+{
#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
static const struct field fields[] = {
{ "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port) },
{ "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src) },
{ "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst) },
{ "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type) },
- { "nw_src", OFPFW_NW_SRC, F_IP, F_OFS(nw_src) },
- { "nw_dst", OFPFW_NW_DST, F_IP, F_OFS(nw_dst) },
+ { "nw_src", OFPFW_NW_SRC_MASK, F_IP,
+ F_OFS(nw_src), OFPFW_NW_SRC_SHIFT },
+ { "nw_dst", OFPFW_NW_DST_MASK, F_IP,
+ F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
{ "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) },
};
+ const struct field *f;
+
+ for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
+ if (!strcmp(f->name, name)) {
+ *f_out = f;
+ return true;
+ }
+ }
+ *f_out = NULL;
+ return false;
+}
+
+static void
+str_to_flow(char *string, struct ofp_match *match,
+ struct ofp_action *action, int *n_actions, uint8_t *table_idx,
+ uint16_t *priority, uint16_t *idle_timeout, uint16_t *hard_timeout)
+{
- char *name, *value;
+ char *name;
uint32_t wildcards;
- char *act_str;
if (table_idx) {
*table_idx = 0xff;
if (priority) {
*priority = OFP_DEFAULT_PRIORITY;
}
- if (max_idle) {
- *max_idle = DEFAULT_MAX_IDLE;
+ if (idle_timeout) {
+ *idle_timeout = DEFAULT_IDLE_TIMEOUT;
+ }
+ if (hard_timeout) {
+ *hard_timeout = OFP_FLOW_PERMANENT;
}
if (action) {
- act_str = strstr(string, "action");
+ char *act_str = strstr(string, "action");
if (!act_str) {
fatal(0, "must specify an action");
}
}
memset(match, 0, sizeof *match);
wildcards = OFPFW_ALL;
- for (name = strtok(string, "="), value = strtok(NULL, ", \t\r\n");
- name && value;
- name = strtok(NULL, "="), value = strtok(NULL, ", \t\r\n"))
- {
- const struct field *f;
- void *data;
-
- if (table_idx && !strcmp(name, "table")) {
- *table_idx = atoi(value);
- continue;
- }
-
- if (priority && !strcmp(name, "priority")) {
- *priority = atoi(value);
- continue;
- }
-
- if (max_idle && !strcmp(name, "max_idle")) {
- *max_idle = atoi(value);
- continue;
- }
-
- for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
- if (!strcmp(f->name, name)) {
- goto found;
- }
- }
- fprintf(stderr, "%s: unknown field %s (fields are",
- program_name, name);
- for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
- if (f != fields) {
- putc(',', stderr);
+ for (name = strtok(string, "=, \t\r\n"); name;
+ name = strtok(NULL, "=, \t\r\n")) {
+ const struct protocol *p;
+
+ if (parse_protocol(name, &p)) {
+ wildcards &= ~OFPFW_DL_TYPE;
+ match->dl_type = htons(p->dl_type);
+ if (p->nw_proto) {
+ wildcards &= ~OFPFW_NW_PROTO;
+ match->nw_proto = p->nw_proto;
}
- fprintf(stderr, " %s", f->name);
- }
- fprintf(stderr, ")\n");
- exit(1);
-
- found:
- data = (char *) match + f->offset;
- if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
- wildcards |= f->wildcard;
} else {
- wildcards &= ~f->wildcard;
- if (f->type == F_U8) {
- *(uint8_t *) data = str_to_int(value);
- } else if (f->type == F_U16) {
- *(uint16_t *) data = htons(str_to_int(value));
- } else if (f->type == F_MAC) {
- str_to_mac(value, data);
- } else if (f->type == F_IP) {
- str_to_ip(value, data);
+ const struct field *f;
+ char *value;
+
+ value = strtok(NULL, ", \t\r\n");
+ if (!value) {
+ fatal(0, "field %s missing value", name);
+ }
+
+ if (table_idx && !strcmp(name, "table")) {
+ *table_idx = atoi(value);
+ } else if (priority && !strcmp(name, "priority")) {
+ *priority = atoi(value);
+ } else if (idle_timeout && !strcmp(name, "idle_timeout")) {
+ *idle_timeout = atoi(value);
+ } else if (hard_timeout && !strcmp(name, "hard_timeout")) {
+ *hard_timeout = atoi(value);
+ } else if (parse_field(name, &f)) {
+ void *data = (char *) match + f->offset;
+ if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
+ wildcards |= f->wildcard;
+ } else {
+ wildcards &= ~f->wildcard;
+ if (f->type == F_U8) {
+ *(uint8_t *) data = str_to_int(value);
+ } else if (f->type == F_U16) {
+ *(uint16_t *) data = htons(str_to_int(value));
+ } else if (f->type == F_MAC) {
+ str_to_mac(value, data);
+ } else if (f->type == F_IP) {
+ wildcards |= str_to_ip(value, data) << f->shift;
+ } else {
+ NOT_REACHED();
+ }
+ }
} else {
- NOT_REACHED();
+ fatal(0, "unknown keyword %s", name);
}
}
}
- if (name && !value) {
- fatal(0, "field %s missing value", name);
- }
- match->wildcards = htons(wildcards);
+ match->wildcards = htonl(wildcards);
}
static void do_dump_flows(int argc, char *argv[])
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);
+ &req->table_id, NULL, NULL, NULL);
memset(req->pad, 0, sizeof req->pad);
dump_stats_transaction(argv[1], request);
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);
+ &req->table_id, NULL, NULL, NULL);
memset(req->pad, 0, sizeof req->pad);
dump_stats_transaction(argv[1], request);
struct vconn *vconn;
struct buffer *buffer;
struct ofp_flow_mod *ofm;
- uint16_t priority, max_idle;
+ uint16_t priority, idle_timeout, hard_timeout;
size_t size;
int n_actions = MAX_ADD_ACTS;
size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions,
- NULL, &priority, &max_idle);
+ NULL, &priority, &idle_timeout, &hard_timeout);
ofm->command = htons(OFPFC_ADD);
- ofm->max_idle = htons(max_idle);
+ ofm->idle_timeout = htons(idle_timeout);
+ ofm->hard_timeout = htons(hard_timeout);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
while (fgets(line, sizeof line, file)) {
struct buffer *buffer;
struct ofp_flow_mod *ofm;
- uint16_t priority, max_idle;
+ uint16_t priority, idle_timeout, hard_timeout;
size_t size;
int n_actions = MAX_ADD_ACTS;
size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(line, &ofm->match, &ofm->actions[0], &n_actions,
- NULL, &priority, &max_idle);
+ NULL, &priority, &idle_timeout, &hard_timeout);
ofm->command = htons(OFPFC_ADD);
- ofm->max_idle = htons(max_idle);
+ ofm->idle_timeout = htons(idle_timeout);
+ ofm->hard_timeout = htons(hard_timeout);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
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);
+ &priority, NULL, NULL);
ofm->command = htons(OFPFC_DELETE);
- ofm->max_idle = htons(0);
+ ofm->idle_timeout = htons(0);
+ ofm->hard_timeout = htons(0);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
vconn_close(vconn);
}
+static void
+do_mod_port(int argc, char *argv[])
+{
+ struct buffer *request, *reply;
+ struct ofp_switch_features *osf;
+ struct ofp_port_mod *opm;
+ struct vconn *vconn;
+ char *endptr;
+ int n_ports;
+ int port_idx;
+ int port_no;
+
+
+ /* Check if the argument is a port index. Otherwise, treat it as
+ * the port name. */
+ port_no = strtol(argv[2], &endptr, 10);
+ if (port_no == 0 && endptr == argv[2]) {
+ port_no = -1;
+ }
+
+ /* Send a "Features Request" to get the information we need in order
+ * to modify the port. */
+ make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
+ run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
+ run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
+
+ osf = reply->data;
+ n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports;
+
+ for (port_idx = 0; port_idx < n_ports; port_idx++) {
+ if (port_no != -1) {
+ /* Check argument as a port index */
+ if (osf->ports[port_idx].port_no == htons(port_no)) {
+ break;
+ }
+ } else {
+ /* Check argument as an interface name */
+ if (!strncmp((char *)osf->ports[port_idx].name, argv[2],
+ sizeof osf->ports[0].name)) {
+ break;
+ }
+
+ }
+ }
+ if (port_idx == n_ports) {
+ fatal(0, "couldn't find monitored port: %s", argv[2]);
+ }
+
+ opm = make_openflow(sizeof(struct ofp_port_mod), OFPT_PORT_MOD, &request);
+ memcpy(&opm->desc, &osf->ports[port_idx], sizeof osf->ports[0]);
+ opm->mask = 0;
+ opm->desc.flags = 0;
+
+ printf("modifying port: %s\n", osf->ports[port_idx].name);
+
+ if (!strncasecmp(argv[3], MOD_PORT_CMD_UP, sizeof MOD_PORT_CMD_UP)) {
+ opm->mask |= htonl(OFPPFL_PORT_DOWN);
+ } else if (!strncasecmp(argv[3], MOD_PORT_CMD_DOWN,
+ sizeof MOD_PORT_CMD_DOWN)) {
+ opm->mask |= htonl(OFPPFL_PORT_DOWN);
+ opm->desc.flags |= htonl(OFPPFL_PORT_DOWN);
+ } else if (!strncasecmp(argv[3], MOD_PORT_CMD_FLOOD,
+ sizeof MOD_PORT_CMD_FLOOD)) {
+ opm->mask |= htonl(OFPPFL_NO_FLOOD);
+ } else if (!strncasecmp(argv[3], MOD_PORT_CMD_NOFLOOD,
+ sizeof MOD_PORT_CMD_NOFLOOD)) {
+ opm->mask |= htonl(OFPPFL_NO_FLOOD);
+ opm->desc.flags |= htonl(OFPPFL_NO_FLOOD);
+ } else {
+ fatal(0, "unknown mod-port command '%s'", argv[3]);
+ }
+
+ send_openflow_buffer(vconn, request);
+
+ buffer_delete(reply);
+ vconn_close(vconn);
+}
+
static void
do_ping(int argc, char *argv[])
{
#endif
{ "show", 1, 1, do_show },
+ { "status", 1, 2, do_status },
{ "help", 0, INT_MAX, do_help },
{ "monitor", 1, 1, do_monitor },
+ { "dump-version", 1, 1, do_dump_version },
{ "dump-tables", 1, 1, do_dump_tables },
{ "dump-flows", 1, 2, do_dump_flows },
{ "dump-aggregate", 1, 2, do_dump_aggregate },
{ "add-flows", 2, 2, do_add_flows },
{ "del-flows", 1, 2, do_del_flows },
{ "dump-ports", 1, 1, do_dump_ports },
+ { "mod-port", 3, 3, do_mod_port },
{ "probe", 1, 1, do_probe },
{ "ping", 1, 2, do_ping },
{ "benchmark", 3, 3, do_benchmark },