*/
#include <config.h>
+#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/time.h>
-#include "command-line.h"
-#include "compiler.h"
-#include "buffer.h"
-#include "dpif.h"
#ifdef HAVE_NETLINK
+#include "netdev.h"
#include "netlink.h"
#include "openflow-netlink.h"
#endif
-#include "util.h"
-#include "socket-util.h"
-#include "openflow.h"
+
+#include "buffer.h"
+#include "command-line.h"
+#include "compiler.h"
+#include "dpif.h"
#include "ofp-print.h"
+#include "openflow.h"
+#include "packets.h"
#include "random.h"
+#include "socket-util.h"
#include "timeval.h"
-#include "vconn.h"
+#include "util.h"
#include "vconn-ssl.h"
+#include "vconn.h"
#include "vlog.h"
#define THIS_MODULE VLM_dpctl
#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;
time_init();
vlog_init();
parse_options(argc, argv);
+ signal(SIGPIPE, SIG_IGN);
argc -= optind;
argv += optind;
"\nFor local datapaths only:\n"
" adddp nl:DP_ID add a new local datapath DP_ID\n"
" deldp nl:DP_ID delete local datapath DP_ID\n"
- " addif nl:DP_ID IFACE add IFACE as a port on DP_ID\n"
- " delif nl:DP_ID IFACE delete IFACE as a port on DP_ID\n"
+ " addif nl:DP_ID IFACE... add each IFACE as a port on DP_ID\n"
+ " delif nl:DP_ID IFACE... delete each IFACE from DP_ID\n"
" 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"
#ifdef HAVE_NETLINK
/* Netlink-only commands. */
-static int if_up(const char* intf)
+static int if_up(const char *netdev_name)
{
- char command[256];
- snprintf(command, sizeof command, "%s %s up &> /dev/null",
- ifconfigbin, intf);
- return system(command);
+ struct netdev *netdev;
+ int retval;
+
+ retval = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev);
+ if (!retval) {
+ retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
+ netdev_close(netdev);
+ }
+ return retval;
}
static void open_nl_vconn(const char *name, bool subscribe, struct dpif *dpif)
dpif_close(&dp);
}
-static void do_add_port(int argc UNUSED, char *argv[])
+static void add_del_ports(int argc UNUSED, char *argv[],
+ int (*function)(struct dpif *, const char *netdev),
+ const char *operation, const char *preposition)
{
struct dpif dp;
- if_up(argv[2]);
+ bool failure = false;
+ int i;
+
open_nl_vconn(argv[1], false, &dp);
- run(dpif_add_port(&dp, argv[2]), "add_port");
+ for (i = 2; i < argc; i++) {
+ int retval = function(&dp, argv[i]);
+ if (retval) {
+ error(retval, "failed to %s %s %s %s",
+ operation, argv[i], preposition, argv[1]);
+ failure = true;
+ }
+ }
dpif_close(&dp);
+ if (failure) {
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int ifup_and_add_port(struct dpif *dpif, const char *netdev)
+{
+ int retval = if_up(netdev);
+ return retval ? retval : dpif_add_port(dpif, netdev);
+}
+
+static void do_add_port(int argc UNUSED, char *argv[])
+{
+ add_del_ports(argc, argv, ifup_and_add_port, "add", "to");
}
static void do_del_port(int argc UNUSED, char *argv[])
{
- struct dpif dp;
- open_nl_vconn(argv[1], false, &dp);
- run(dpif_del_port(&dp, argv[2]), "del_port");
- dpif_close(&dp);
+ add_del_ports(argc, argv, dpif_del_port, "remove", "from");
}
static void do_monitor(int argc UNUSED, char *argv[])
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 *idle_timeout, uint16_t *hard_timeout)
+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)
{
- struct field {
- const char *name;
- uint32_t wildcard;
- enum { F_U8, F_U16, F_MAC, F_IP } type;
- size_t offset;
+ 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;
*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 (idle_timeout && !strcmp(name, "idle_timeout")) {
- *idle_timeout = atoi(value);
- continue;
- }
-
- if (hard_timeout && !strcmp(name, "hard_timeout")) {
- *hard_timeout = 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[])
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[])
{
#ifdef HAVE_NETLINK
{ "adddp", 1, 1, do_add_dp },
{ "deldp", 1, 1, do_del_dp },
- { "addif", 2, 2, do_add_port },
- { "delif", 2, 2, do_del_port },
+ { "addif", 2, INT_MAX, do_add_port },
+ { "delif", 2, INT_MAX, do_del_port },
#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 },