From: Ben Pfaff Date: Mon, 15 Sep 2008 22:31:36 +0000 (-0700) Subject: Merge branch "partner", to simplify partner integration. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f593e3ab70ab06acbf97cbb0a9f897dc4f078011;p=openvswitch Merge branch "partner", to simplify partner integration. --- f593e3ab70ab06acbf97cbb0a9f897dc4f078011 diff --cc controller/controller.c index e894cf41,a3ce2bd9..1bed5c61 --- a/controller/controller.c +++ b/controller/controller.c @@@ -111,25 -112,22 +112,25 @@@ main(int argc, char *argv[] struct vconn *vconn; int retval; - retval = vconn_open(name, &vconn); - if (retval) { - VLOG_ERR("%s: connect: %s", name, strerror(retval)); - continue; - } - - if (vconn_is_passive(vconn)) { - if (n_listeners >= MAX_LISTENERS) { - ofp_fatal(0, "max %d passive connections", n_listeners); - } - listeners[n_listeners++] = vconn; - } else { + retval = vconn_open(name, OFP_VERSION, &vconn); + if (!retval) { if (n_switches >= MAX_SWITCHES) { - fatal(0, "max %d switch connections", n_switches); + ofp_fatal(0, "max %d switch connections", n_switches); } new_switch(&switches[n_switches++], vconn, name); + continue; + } else if (retval == EAFNOSUPPORT) { + struct pvconn *pvconn; + retval = pvconn_open(name, &pvconn); + if (!retval) { + if (n_listeners >= MAX_LISTENERS) { - fatal(0, "max %d passive connections", n_listeners); ++ ofp_fatal(0, "max %d passive connections", n_listeners); + } + listeners[n_listeners++] = pvconn; + } + } + if (retval) { + VLOG_ERR("%s: connect: %s", name, strerror(retval)); } } if (n_switches == 0 && n_listeners == 0) { diff --cc include/vconn-provider.h index a33fa492,3a40e234..cf033afc --- a/include/vconn-provider.h +++ b/include/vconn-provider.h @@@ -94,96 -81,49 +94,96 @@@ struct vconn_class * complete; instead, it should return EAGAIN immediately. */ int (*connect)(struct vconn *vconn); - /* Tries to accept a new connection on 'vconn', which must be a passive - * vconn. If successful, stores the new connection in '*new_vconnp' and - * returns 0. Otherwise, returns a positive errno value. - * - * The accept function must not block waiting for a connection. If no - * connection is ready to be accepted, it should return EAGAIN. - * - * Nonnull iff this is a passive vconn (one that accepts connections and - * does not transfer data). */ - int (*accept)(struct vconn *vconn, struct vconn **new_vconnp); - - /* Tries to receive an OpenFlow message from 'vconn', which must be an - * active vconn. If successful, stores the received message into '*msgp' - * and returns 0. The caller is responsible for destroying the message - * with ofpbuf_delete(). On failure, returns a positive errno value and - * stores a null pointer into '*msgp'. + /* Tries to receive an OpenFlow message from 'vconn'. If successful, + * stores the received message into '*msgp' and returns 0. The caller is - * responsible for destroying the message with buffer_delete(). On ++ * responsible for destroying the message with ofpbuf_delete(). On + * failure, returns a positive errno value and stores a null pointer into + * '*msgp'. * * If the connection has been closed in the normal fashion, returns EOF. * * The recv function must not block waiting for a packet to arrive. If no - * packets have been received, it should return EAGAIN. - * - * Nonnull iff this is an active vconn (one that transfers data and does - * not accept connections). */ + * packets have been received, it should return EAGAIN. */ - int (*recv)(struct vconn *vconn, struct buffer **msgp); + int (*recv)(struct vconn *vconn, struct ofpbuf **msgp); - /* Tries to queue 'msg' for transmission on 'vconn', which must be an - * active vconn. If successful, returns 0, in which case ownership of - * 'msg' is transferred to the vconn. Success does not guarantee that - * 'msg' has been or ever will be delivered to the peer, only that it has - * been queued for transmission. + /* Tries to queue 'msg' for transmission on 'vconn'. If successful, + * returns 0, in which case ownership of 'msg' is transferred to the vconn. + * Success does not guarantee that 'msg' has been or ever will be delivered + * to the peer, only that it has been queued for transmission. * * Returns a positive errno value on failure, in which case the caller * retains ownership of 'msg'. * * The send function must not block. If 'msg' cannot be immediately - * accepted for transmission, it should return EAGAIN. - * - * Nonnull iff this is an active vconn (one that transfers data and does - * not accept connections). */ + * accepted for transmission, it should return EAGAIN. */ - int (*send)(struct vconn *vconn, struct buffer *msg); + int (*send)(struct vconn *vconn, struct ofpbuf *msg); - void (*wait)(struct vconn *vconn, enum vconn_wait_type); + /* Arranges for the poll loop to wake up when 'vconn' is ready to take an + * action of the given 'type'. */ + void (*wait)(struct vconn *vconn, enum vconn_wait_type type); +}; + +/* Passive virtual connection to an OpenFlow device. + * + * This structure should be treated as opaque by vconn implementations. */ +struct pvconn { + struct pvconn_class *class; + char *name; }; +void pvconn_init(struct pvconn *, struct pvconn_class *, const char *name); +static inline void pvconn_assert_class(const struct pvconn *pvconn, + const struct pvconn_class *class) +{ + assert(pvconn->class == class); +} + +struct pvconn_class { + /* Prefix for connection names, e.g. "ptcp", "pssl". */ + const char *name; + + /* Attempts to start listening for OpenFlow connections. 'name' is the + * full connection name provided by the user, e.g. "nl:0", "tcp:1.2.3.4". + * This name is useful for error messages but must not be modified. + * + * 'suffix' is a copy of 'name' following the colon and may be modified. + * + * Returns 0 if successful, otherwise a positive errno value. If + * successful, stores a pointer to the new connection in '*pvconnp'. + * + * The listen function must not block. If the connection cannot be + * completed immediately, it should return EAGAIN (not EINPROGRESS, as + * returned by the connect system call) and continue the connection in the + * background. */ + int (*listen)(const char *name, char *suffix, struct pvconn **pvconnp); + + /* Closes 'pvconn' and frees associated memory. */ + void (*close)(struct pvconn *pvconn); + + /* Tries to accept a new connection on 'pvconn'. If successful, stores the + * new connection in '*new_vconnp' and returns 0. Otherwise, returns a + * positive errno value. + * + * The accept function must not block waiting for a connection. If no + * connection is ready to be accepted, it should return EAGAIN. */ + int (*accept)(struct pvconn *pvconn, struct vconn **new_vconnp); + + /* Arranges for the poll loop to wake up when a connection is ready to be + * accepted on 'pvconn'. */ + void (*wait)(struct pvconn *pvconn); +}; + +/* Active and passive vconn classes. */ +extern struct vconn_class tcp_vconn_class; +extern struct pvconn_class ptcp_pvconn_class; +extern struct vconn_class unix_vconn_class; +extern struct pvconn_class punix_pvconn_class; +#ifdef HAVE_OPENSSL +extern struct vconn_class ssl_vconn_class; +extern struct pvconn_class pssl_pvconn_class; +#endif +#ifdef HAVE_NETLINK +extern struct vconn_class netlink_vconn_class; +#endif + #endif /* vconn-provider.h */ diff --cc include/vconn.h index e9eab5e4,224fd561..91bb6879 --- a/include/vconn.h +++ b/include/vconn.h @@@ -38,27 -38,29 +38,27 @@@ #include #include - struct buffer; + struct ofpbuf; struct flow; -struct pollfd; struct ofp_header; +struct pvconn; struct vconn; -/* Client interface to vconns, which provide a virtual connection to an - * OpenFlow device. */ - void vconn_usage(bool active, bool passive); -int vconn_open(const char *name, struct vconn **); + +/* Active vconns: virtual connections to OpenFlow devices. */ +int vconn_open(const char *name, int min_version, struct vconn **); void vconn_close(struct vconn *); -bool vconn_is_passive(const struct vconn *); +const char *vconn_get_name(const struct vconn *); uint32_t vconn_get_ip(const struct vconn *); int vconn_connect(struct vconn *); - int vconn_recv(struct vconn *, struct buffer **); - int vconn_send(struct vconn *, struct buffer *); - int vconn_transact(struct vconn *, struct buffer *, struct buffer **); -int vconn_accept(struct vconn *, struct vconn **); + int vconn_recv(struct vconn *, struct ofpbuf **); + int vconn_send(struct vconn *, struct ofpbuf *); + int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **); -int vconn_open_block(const char *name, struct vconn **); +int vconn_open_block(const char *name, int min_version, struct vconn **); - int vconn_send_block(struct vconn *, struct buffer *); - int vconn_recv_block(struct vconn *, struct buffer **); + int vconn_send_block(struct vconn *, struct ofpbuf *); + int vconn_recv_block(struct vconn *, struct ofpbuf **); enum vconn_wait_type { WAIT_CONNECT, @@@ -70,27 -73,33 +70,27 @@@ void vconn_connect_wait(struct vconn *) void vconn_recv_wait(struct vconn *); void vconn_send_wait(struct vconn *); +/* Passive vconns: virtual listeners for incoming OpenFlow connections. */ +int pvconn_open(const char *name, struct pvconn **); +void pvconn_close(struct pvconn *); +int pvconn_accept(struct pvconn *, int min_version, struct vconn **); +void pvconn_wait(struct pvconn *); + +/* OpenFlow protocol utility functions. */ - void *make_openflow(size_t openflow_len, uint8_t type, struct buffer **); + void *make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **); void *make_openflow_xid(size_t openflow_len, uint8_t type, - uint32_t xid, struct buffer **); - void update_openflow_length(struct buffer *); - struct buffer *make_add_flow(const struct flow *, uint32_t buffer_id, + uint32_t xid, struct ofpbuf **); + void update_openflow_length(struct ofpbuf *); + struct ofpbuf *make_add_flow(const struct flow *, uint32_t buffer_id, uint16_t max_idle, size_t n_actions); - struct buffer *make_add_simple_flow(const struct flow *, + struct ofpbuf *make_add_simple_flow(const struct flow *, uint32_t buffer_id, uint16_t out_port, uint16_t max_idle); - struct buffer *make_buffered_packet_out(uint32_t buffer_id, + struct ofpbuf *make_buffered_packet_out(uint32_t buffer_id, uint16_t in_port, uint16_t out_port); - struct buffer *make_unbuffered_packet_out(const struct buffer *packet, + struct ofpbuf *make_unbuffered_packet_out(const struct ofpbuf *packet, uint16_t in_port, uint16_t out_port); - struct buffer *make_echo_request(void); - struct buffer *make_echo_reply(const struct ofp_header *rq); + struct ofpbuf *make_echo_request(void); + struct ofpbuf *make_echo_reply(const struct ofp_header *rq); -extern struct vconn_class tcp_vconn_class; -extern struct vconn_class ptcp_vconn_class; -extern struct vconn_class unix_vconn_class; -extern struct vconn_class punix_vconn_class; -#ifdef HAVE_OPENSSL -extern struct vconn_class ssl_vconn_class; -extern struct vconn_class pssl_vconn_class; -#endif -#ifdef HAVE_NETLINK -extern struct vconn_class netlink_vconn_class; -#endif - #endif /* vconn.h */ diff --cc lib/learning-switch.c index d0990261,e1184051..fedf73ee --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@@ -73,10 -72,8 +73,10 @@@ struct lswitch * rate limit relatively high. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); - static void queue_tx(struct lswitch *, struct rconn *, struct buffer *); + static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *); static void send_features_request(struct lswitch *, struct rconn *); +static void process_switch_features(struct lswitch *, struct rconn *, + struct ofp_switch_features *); static void process_packet_in(struct lswitch *, struct rconn *, struct ofp_packet_in *); static void process_echo_request(struct lswitch *, struct rconn *, @@@ -118,6 -111,14 +118,15 @@@ lswitch_destroy(struct lswitch *sw } } + static size_t + min_size(uint8_t type) + { + return (type == OFPT_FEATURES_REPLY ? sizeof(struct ofp_switch_features) + : type == OFPT_PACKET_IN ? offsetof (struct ofp_packet_in, data) ++ : type == OFPT_PORT_STATUS ? sizeof(struct ofp_port_status) + : sizeof(struct ofp_header)); + } + /* Processes 'msg', which should be an OpenFlow received on 'rconn', according * to the learning switch state in 'sw'. The most likely result of processing * is that flow-setup and packet-out OpenFlow messages will be sent out on @@@ -167,7 -161,8 +170,7 @@@ send_features_request(struct lswitch *s { time_t now = time_now(); if (now >= sw->last_features_request + 1) { - struct buffer *b; + struct ofpbuf *b; - struct ofp_header *ofr; struct ofp_switch_config *osc; /* Send OFPT_FEATURES_REQUEST. */ @@@ -276,62 -265,3 +279,62 @@@ process_echo_request(struct lswitch *sw { queue_tx(sw, rconn, make_echo_reply(rq)); } + +static void +process_port_status(struct lswitch *sw, struct rconn *rconn, + struct ofp_port_status *ops) +{ + process_phy_port(sw, rconn, &ops->desc); +} + +static void +process_phy_port(struct lswitch *sw, struct rconn *rconn, + const struct ofp_phy_port *opp) +{ + if (sw->capabilities & OFPC_STP && opp->features & ntohl(OFPPF_STP)) { + uint32_t flags = ntohl(opp->flags); + uint32_t new_flags = flags & ~(OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP + | OFPPFL_NO_FWD | OFPPFL_NO_PACKET_IN); + if (!(flags & (OFPPFL_NO_STP | OFPPFL_PORT_DOWN | OFPPFL_LINK_DOWN))) { + bool forward = false; + bool learn = false; + switch (flags & OFPPFL_STP_MASK) { + case OFPPFL_STP_LISTEN: + case OFPPFL_STP_BLOCK: + break; + case OFPPFL_STP_LEARN: + learn = true; + break; + case OFPPFL_STP_FORWARD: + forward = learn = true; + break; + } + if (!forward) { + new_flags |= OFPPFL_NO_RECV | OFPPFL_NO_FWD; + } + if (!learn) { + new_flags |= OFPPFL_NO_PACKET_IN; + } + } + if (flags != new_flags) { + struct ofp_port_mod *opm; - struct buffer *b; ++ struct ofpbuf *b; + int retval; + + VLOG_WARN("port %d: flags=%x new_flags=%x", + ntohs(opp->port_no), flags, new_flags); + opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b); + opm->mask = htonl(flags ^ new_flags); + opm->desc = *opp; + opm->desc.flags = htonl(new_flags); + retval = rconn_send(rconn, b, NULL); + if (retval) { + if (retval != ENOTCONN) { + VLOG_WARN_RL(&rl, "%s: send: %s", + rconn_get_name(rconn), strerror(retval)); + } - buffer_delete(b); ++ ofpbuf_delete(b); + } + } + } +} diff --cc lib/ofp-print.c index 1f16396a,6f0957a5..e78a24aa --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@@ -940,11 -890,18 +946,18 @@@ print_stats(struct ds *string, int type { 0, 0, NULL, }, { 0, SIZE_MAX, ofp_port_stats_reply }, }, - [OFPST_VENDOR] = { + { - OFPST_SWITCH, - "switch status", - { 0, 0, NULL, }, - { 0, SIZE_MAX, switch_status_reply }, ++ OFPST_VENDOR, + "vendor-specific", + { sizeof(uint32_t), SIZE_MAX, vendor_stat }, + { sizeof(uint32_t), SIZE_MAX, vendor_stat }, }, + { + -1, + "unknown", + { 0, 0, NULL, }, + { 0, 0, NULL, }, + }, }; const struct stats_type *s; @@@ -1025,12 -987,8 +1043,14 @@@ struct openflow_packet }; static const struct openflow_packet packets[] = { - [OFPT_HELLO] = { ++ { ++ OFPT_HELLO, + "hello", + sizeof (struct ofp_header), + NULL, + }, - [OFPT_FEATURES_REQUEST] = { + { + OFPT_FEATURES_REQUEST, "features_request", sizeof (struct ofp_header), NULL, @@@ -1085,7 -1053,8 +1115,8 @@@ sizeof (struct ofp_port_status), ofp_print_port_status }, - [OFPT_ERROR] = { + { - OFPT_ERROR_MSG, ++ OFPT_ERROR, "error_msg", sizeof (struct ofp_error_msg), ofp_print_error_msg, diff --cc lib/rconn.c index c29e69a1,7eb93baf..fabd61ee --- a/lib/rconn.c +++ b/lib/rconn.c @@@ -38,10 -38,8 +38,9 @@@ #include #include #include - #include "buffer.h" + #include "ofpbuf.h" +#include "openflow.h" #include "poll-loop.h" - #include "ofp-print.h" #include "sat-math.h" #include "timeval.h" #include "util.h" @@@ -131,7 -124,6 +130,7 @@@ static int reconnect(struct rconn *) static void disconnect(struct rconn *, int error); static void flush_queue(struct rconn *); static void question_connectivity(struct rconn *); - static void copy_to_monitor(struct rconn *, const struct buffer *); ++static void copy_to_monitor(struct rconn *, const struct ofpbuf *); /* Creates a new rconn, connects it (reliably) to 'name', and returns it. */ struct rconn * @@@ -430,10 -425,9 +429,10 @@@ struct ofpbuf rconn_recv(struct rconn *rc) { if (rc->state & (S_ACTIVE | S_IDLE)) { - struct buffer *buffer; + struct ofpbuf *buffer; int error = vconn_recv(rc->vconn, &buffer); if (!error) { + copy_to_monitor(rc, buffer); rc->last_received = time_now(); rc->packets_received++; if (rc->state == S_IDLE) { @@@ -471,10 -465,9 +470,10 @@@ rconn_recv_wait(struct rconn *rc * takes care of sending if you call rconn_run(), which will have the side * effect of waking up poll_block(). */ int - rconn_send(struct rconn *rc, struct buffer *b, int *n_queued) + rconn_send(struct rconn *rc, struct ofpbuf *b, int *n_queued) { if (rconn_is_connected(rc)) { + copy_to_monitor(rc, b); b->private = n_queued; if (n_queued) { ++*n_queued; @@@ -788,31 -766,3 +787,31 @@@ question_connectivity(struct rconn *rc rc->last_questioned = now; } } + +static void - copy_to_monitor(struct rconn *rc, const struct buffer *b) ++copy_to_monitor(struct rconn *rc, const struct ofpbuf *b) +{ - struct buffer *clone = NULL; ++ struct ofpbuf *clone = NULL; + int retval; + size_t i; + + for (i = 0; i < rc->n_monitors; ) { + struct vconn *vconn = rc->monitors[i]; + + if (!clone) { - clone = buffer_clone(b); ++ clone = ofpbuf_clone(b); + } + retval = vconn_send(vconn, clone); + if (!retval) { + clone = NULL; + } else if (retval != EAGAIN) { + VLOG_DBG("%s: closing monitor connection to %s: %s", + rconn_get_name(rc), vconn_get_name(vconn), + strerror(retval)); + rc->monitors[i] = rc->monitors[--rc->n_monitors]; + continue; + } + i++; + } - buffer_delete(clone); ++ ofpbuf_delete(clone); +} diff --cc lib/vconn-netlink.c index 29212369,aabd01ba..0149a37a --- a/lib/vconn-netlink.c +++ b/lib/vconn-netlink.c @@@ -142,10 -142,12 +142,11 @@@ netlink_wait(struct vconn *vconn, enum } struct vconn_class netlink_vconn_class = { - .name = "nl", - .open = netlink_open, - .close = netlink_close, - .recv = netlink_recv, - .send = netlink_send, - .wait = netlink_wait, + "nl", /* name */ + netlink_open, /* open */ + netlink_close, /* close */ + NULL, /* connect */ - NULL, /* accept */ + netlink_recv, /* recv */ + netlink_send, /* send */ + netlink_wait, /* wait */ }; diff --cc lib/vconn-ssl.c index bac39b5e,ef5885be..2801c0ca --- a/lib/vconn-ssl.c +++ b/lib/vconn-ssl.c @@@ -652,13 -651,14 +651,13 @@@ ssl_wait(struct vconn *vconn, enum vcon } struct vconn_class ssl_vconn_class = { - .name = "ssl", - .open = ssl_open, - .close = ssl_close, - .connect = ssl_connect, - .recv = ssl_recv, - .send = ssl_send, - .wait = ssl_wait, + "ssl", /* name */ + ssl_open, /* open */ + ssl_close, /* close */ + ssl_connect, /* connect */ - NULL, /* accept */ + ssl_recv, /* recv */ + ssl_send, /* send */ + ssl_wait, /* wait */ }; /* Passive SSL. */ diff --cc lib/vconn-stream.c index 5cf296fc,83795097..f5fe9476 --- a/lib/vconn-stream.c +++ b/lib/vconn-stream.c @@@ -246,12 -246,14 +246,13 @@@ stream_wait(struct vconn *vconn, enum v } static struct vconn_class stream_vconn_class = { - .name = "stream", - .close = stream_close, - .connect = stream_connect, - .recv = stream_recv, - .send = stream_send, - .wait = stream_wait, + "stream", /* name */ + NULL, /* open */ + stream_close, /* close */ + stream_connect, /* connect */ - NULL, /* accept */ + stream_recv, /* recv */ + stream_send, /* send */ + stream_wait, /* wait */ }; /* Passive stream socket vconn. */ diff --cc lib/vconn-tcp.c index bf1851aa,3d336c46..2c8522d2 --- a/lib/vconn-tcp.c +++ b/lib/vconn-tcp.c @@@ -127,8 -127,14 +127,13 @@@ tcp_open(const char *name, char *suffix } struct vconn_class tcp_vconn_class = { - .name = "tcp", - .open = tcp_open, + "tcp", /* name */ + tcp_open, /* open */ + NULL, /* close */ + NULL, /* connect */ - NULL, /* accept */ + NULL, /* recv */ + NULL, /* send */ + NULL, /* wait */ }; /* Passive TCP. */ diff --cc lib/vconn-unix.c index cff7d2e6,e84674ff..9b08b10e --- a/lib/vconn-unix.c +++ b/lib/vconn-unix.c @@@ -82,8 -81,14 +81,13 @@@ unix_open(const char *name, char *suffi } struct vconn_class unix_vconn_class = { - .name = "unix", - .open = unix_open, + "unix", /* name */ + unix_open, /* open */ + NULL, /* close */ + NULL, /* connect */ - NULL, /* accept */ + NULL, /* recv */ + NULL, /* send */ + NULL, /* wait */ }; /* Passive UNIX socket. */ diff --cc lib/vconn.c index e136e963,7162f6b3..1c3b699c --- a/lib/vconn.c +++ b/lib/vconn.c @@@ -40,10 -40,9 +40,10 @@@ #include #include #include - #include "buffer.h" +#include "dynamic-string.h" #include "flow.h" #include "ofp-print.h" + #include "ofpbuf.h" #include "openflow.h" #include "poll-loop.h" #include "random.h" @@@ -89,9 -70,6 +89,9 @@@ static struct pvconn_class *pvconn_clas * really need to see them. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(600, 600); - static int do_recv(struct vconn *, struct buffer **); - static int do_send(struct vconn *, struct buffer *); ++static int do_recv(struct vconn *, struct ofpbuf **); ++static int do_send(struct vconn *, struct ofpbuf *); + /* Check the validity of the vconn class structures. */ static void check_vconn_classes(void) @@@ -267,157 -235,40 +267,157 @@@ vconn_get_ip(const struct vconn *vconn return vconn->ip; } -/* Tries to complete the connection on 'vconn', which must be an active - * vconn. If 'vconn''s connection is complete, returns 0 if the connection - * was successful or a positive errno value if it failed. If the - * connection is still in progress, returns EAGAIN. */ -int -vconn_connect(struct vconn *vconn) +static void +vcs_connecting(struct vconn *vconn) { - if (vconn->connect_status == EAGAIN) { - vconn->connect_status = (vconn->class->connect)(vconn); - assert(vconn->connect_status != EINPROGRESS); + int retval = (vconn->class->connect)(vconn); + assert(retval != EINPROGRESS); + if (!retval) { + vconn->state = VCS_SEND_HELLO; + } else if (retval != EAGAIN) { + vconn->state = VCS_DISCONNECTED; + vconn->error = retval; } - return vconn->connect_status; } -/* Tries to accept a new connection on 'vconn', which must be a passive vconn. - * If successful, stores the new connection in '*new_vconn' and returns 0. - * Otherwise, returns a positive errno value. - * - * vconn_accept will not block waiting for a connection. If no connection is - * ready to be accepted, it returns EAGAIN immediately. */ -int -vconn_accept(struct vconn *vconn, struct vconn **new_vconn) +static void +vcs_send_hello(struct vconn *vconn) { - struct buffer *b; ++ struct ofpbuf *b; int retval; - retval = (vconn->class->accept)(vconn, new_vconn); + make_openflow(sizeof(struct ofp_header), OFPT_HELLO, &b); + retval = do_send(vconn, b); + if (!retval) { + vconn->state = VCS_RECV_HELLO; + } else { - buffer_delete(b); ++ ofpbuf_delete(b); + if (retval != EAGAIN) { + vconn->state = VCS_DISCONNECTED; + vconn->error = retval; + } + } +} + +static void +vcs_recv_hello(struct vconn *vconn) +{ - struct buffer *b; ++ struct ofpbuf *b; + int retval; + retval = do_recv(vconn, &b); + if (!retval) { + struct ofp_header *oh = b->data; + + if (oh->type == OFPT_HELLO) { + if (b->size > sizeof *oh) { + struct ds msg = DS_EMPTY_INITIALIZER; + ds_put_format(&msg, "%s: extra-long hello:\n", vconn->name); + ds_put_hex_dump(&msg, b->data, b->size, 0, true); + VLOG_WARN_RL(&rl, ds_cstr(&msg)); + ds_destroy(&msg); + } + + vconn->version = MIN(OFP_VERSION, oh->version); + if (vconn->version < vconn->min_version) { + VLOG_WARN_RL(&rl, "%s: version negotiation failed: we support " + "versions 0x%02x to 0x%02x inclusive but peer " + "supports no later than version 0x%02"PRIx8, + vconn->name, vconn->min_version, OFP_VERSION, + oh->version); + vconn->state = VCS_SEND_ERROR; + } else { + VLOG_DBG("%s: negotiated OpenFlow version 0x%02x " + "(we support versions 0x%02x to 0x%02x inclusive, " + "peer no later than version 0x%02"PRIx8")", + vconn->name, vconn->version, vconn->min_version, + OFP_VERSION, oh->version); + vconn->state = VCS_CONNECTED; + } - buffer_delete(b); ++ ofpbuf_delete(b); + return; + } else { + char *s = ofp_to_string(b->data, b->size, 1); + VLOG_WARN_RL(&rl, "%s: received message while expecting hello: %s", + vconn->name, s); + free(s); + retval = EPROTO; - buffer_delete(b); ++ ofpbuf_delete(b); + } + } + + if (retval != EAGAIN) { + vconn->state = VCS_DISCONNECTED; + vconn->error = retval; + } +} + +static void +vcs_send_error(struct vconn *vconn) +{ + struct ofp_error_msg *error; - struct buffer *b; ++ struct ofpbuf *b; + char s[128]; + int retval; + + snprintf(s, sizeof s, "We support versions 0x%02x to 0x%02x inclusive but " + "you support no later than version 0x%02"PRIx8".", + vconn->min_version, OFP_VERSION, vconn->version); + error = make_openflow(sizeof *error, OFPT_ERROR, &b); + error->type = htons(OFPET_HELLO_FAILED); + error->code = htons(OFPHFC_INCOMPATIBLE); - buffer_put(b, s, strlen(s)); ++ ofpbuf_put(b, s, strlen(s)); + retval = do_send(vconn, b); if (retval) { - buffer_delete(b); - *new_vconn = NULL; - } else { - assert((*new_vconn)->connect_status != EAGAIN - || (*new_vconn)->class->connect); ++ ofpbuf_delete(b); + } + if (retval != EAGAIN) { + vconn->state = VCS_DISCONNECTED; + vconn->error = retval ? retval : EPROTO; } - return retval; +} + +/* Tries to complete the connection on 'vconn', which must be an active + * vconn. If 'vconn''s connection is complete, returns 0 if the connection + * was successful or a positive errno value if it failed. If the + * connection is still in progress, returns EAGAIN. */ +int +vconn_connect(struct vconn *vconn) +{ + enum vconn_state last_state; + + assert(vconn->min_version >= 0); + do { + last_state = vconn->state; + switch (vconn->state) { + case VCS_CONNECTING: + vcs_connecting(vconn); + break; + + case VCS_SEND_HELLO: + vcs_send_hello(vconn); + break; + + case VCS_RECV_HELLO: + vcs_recv_hello(vconn); + break; + + case VCS_CONNECTED: + return 0; + + case VCS_SEND_ERROR: + vcs_send_error(vconn); + break; + + case VCS_DISCONNECTED: + return vconn->error; + + default: + NOT_REACHED(); + } + } while (vconn->state != last_state); + + return EAGAIN; } /* Tries to receive an OpenFlow message from 'vconn', which must be an active @@@ -433,45 -284,25 +433,45 @@@ vconn_recv(struct vconn *vconn, struct { int retval = vconn_connect(vconn); if (!retval) { - retval = (vconn->class->recv)(vconn, msgp); - if (!retval) { - struct ofp_header *oh; - - if (VLOG_IS_DBG_ENABLED()) { - char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1); - VLOG_DBG_RL(&rl, "%s: received: %s", vconn->name, s); - free(s); - } + retval = do_recv(vconn, msgp); + } + return retval; +} - oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh); - if (oh->version != OFP_VERSION) { +static int - do_recv(struct vconn *vconn, struct buffer **msgp) ++do_recv(struct vconn *vconn, struct ofpbuf **msgp) +{ + int retval; + + retval = (vconn->class->recv)(vconn, msgp); + if (!retval) { + struct ofp_header *oh; + + if (VLOG_IS_DBG_ENABLED()) { + char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1); + VLOG_DBG_RL(&rl, "%s: received: %s", vconn->name, s); + free(s); + } + - oh = buffer_at_assert(*msgp, 0, sizeof *oh); ++ oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh); + if (oh->version != vconn->version + && oh->type != OFPT_HELLO + && oh->type != OFPT_ERROR + && oh->type != OFPT_ECHO_REQUEST + && oh->type != OFPT_ECHO_REPLY + && oh->type != OFPT_VENDOR) + { + if (vconn->version < 0) { + VLOG_ERR_RL(&rl, "%s: received OpenFlow version %02"PRIx8" " + "before version negotiation complete", + vconn->name, oh->version); + } else { VLOG_ERR_RL(&rl, "%s: received OpenFlow version %02"PRIx8" " "!= expected %02x", - vconn->name, oh->version, OFP_VERSION); - ofpbuf_delete(*msgp); - *msgp = NULL; - return EPROTO; + vconn->name, oh->version, vconn->version); } - buffer_delete(*msgp); ++ ofpbuf_delete(*msgp); + retval = EPROTO; } } if (retval) { @@@ -496,28 -327,18 +496,28 @@@ vconn_send(struct vconn *vconn, struct { int retval = vconn_connect(vconn); if (!retval) { - assert(msg->size >= sizeof(struct ofp_header)); - assert(((struct ofp_header *) msg->data)->length == htons(msg->size)); - if (!VLOG_IS_DBG_ENABLED()) { - retval = (vconn->class->send)(vconn, msg); - } else { - char *s = ofp_to_string(msg->data, msg->size, 1); - retval = (vconn->class->send)(vconn, msg); - if (retval != EAGAIN) { - VLOG_DBG_RL(&rl, "%s: sent (%s): %s", vconn->name, strerror(retval), s); - } - free(s); + retval = do_send(vconn, msg); + } + return retval; +} + +static int - do_send(struct vconn *vconn, struct buffer *msg) ++do_send(struct vconn *vconn, struct ofpbuf *msg) +{ + int retval; + + assert(msg->size >= sizeof(struct ofp_header)); + assert(((struct ofp_header *) msg->data)->length == htons(msg->size)); + if (!VLOG_IS_DBG_ENABLED()) { + retval = (vconn->class->send)(vconn, msg); + } else { + char *s = ofp_to_string(msg->data, msg->size, 1); + retval = (vconn->class->send)(vconn, msg); + if (retval != EAGAIN) { + VLOG_DBG_RL(&rl, "%s: sent (%s): %s", + vconn->name, strerror(retval), s); } + free(s); } return retval; } @@@ -800,9 -543,9 +800,9 @@@ make_unbuffered_packet_out(const struc { struct ofp_packet_out *opo; size_t size = sizeof *opo + sizeof opo->actions[0]; - struct buffer *out = buffer_new(size + packet->size); - opo = buffer_put_uninit(out, size); + struct ofpbuf *out = ofpbuf_new(size + packet->size); + opo = ofpbuf_put_uninit(out, size); - memset(opo, 0, sizeof *opo); + memset(opo, 0, size); opo->header.version = OFP_VERSION; opo->header.type = OFPT_PACKET_OUT; opo->buffer_id = htonl(UINT32_MAX); diff --cc secchan/secchan.c index c0a1c564,9508369a..f00df4a4 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@@ -58,7 -57,7 +57,8 @@@ #include "list.h" #include "mac-learning.h" #include "netdev.h" +#include "nicira-ext.h" + #include "ofpbuf.h" #include "openflow.h" #include "packets.h" #include "poll-loop.h" @@@ -155,10 -148,6 +155,10 @@@ static struct hook make_hook(bool (*loc void (*periodic_cb)(void *), void (*wait_cb)(void *), void *aux); +static struct ofp_packet_in *get_ofp_packet_in(struct relay *); +static bool get_ofp_packet_eth_header(struct relay *, struct ofp_packet_in **, + struct eth_header **); - static void get_ofp_packet_payload(struct ofp_packet_in *, struct buffer *); ++static void get_ofp_packet_payload(struct ofp_packet_in *, struct ofpbuf *); struct switch_status; struct status_reply; @@@ -369,35 -338,8 +369,35 @@@ main(int argc, char *argv[] return 0; } +static struct pvconn * +open_passive_vconn(const char *name) +{ + struct pvconn *pvconn; + int retval; + + retval = pvconn_open(name, &pvconn); + if (retval && retval != EAGAIN) { - fatal(retval, "opening %s", name); ++ ofp_fatal(retval, "opening %s", name); + } + return pvconn; +} + +static struct vconn * +accept_vconn(struct pvconn *pvconn) +{ + struct vconn *new; + int retval; + + retval = pvconn_accept(pvconn, OFP_VERSION, &new); + if (retval && retval != EAGAIN) { + VLOG_WARN_RL(&vrl, "accept failed (%s)", strerror(retval)); + } + return new; +} + static struct hook -make_hook(bool (*packet_cb)(struct relay *, int half, void *aux), +make_hook(bool (*local_packet_cb)(struct relay *, void *aux), + bool (*remote_packet_cb)(struct relay *, void *aux), void (*periodic_cb)(void *aux), void (*wait_cb)(void *aux), void *aux) @@@ -410,37 -351,6 +410,37 @@@ h.aux = aux; return h; } + +static struct ofp_packet_in * +get_ofp_packet_in(struct relay *r) +{ - struct buffer *msg = r->halves[HALF_LOCAL].rxbuf; ++ struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf; + struct ofp_header *oh = msg->data; + if (oh->type == OFPT_PACKET_IN) { + if (msg->size >= offsetof (struct ofp_packet_in, data)) { + return msg->data; + } else { + VLOG_WARN("packet too short (%zu bytes) for packet_in", + msg->size); + } + } + return NULL; +} + +static bool +get_ofp_packet_eth_header(struct relay *r, struct ofp_packet_in **opip, + struct eth_header **ethp) +{ + const int min_len = offsetof(struct ofp_packet_in, data) + ETH_HEADER_LEN; + struct ofp_packet_in *opi = get_ofp_packet_in(r); + if (opi && ntohs(opi->header.length) >= min_len) { + *opip = opi; + *ethp = (void *) opi->data; + return true; + } + return false; +} + /* OpenFlow message relaying. */ @@@ -513,11 -426,11 +513,11 @@@ relay_run(struct relay *r, const struc if (!this->rxbuf) { this->rxbuf = rconn_recv(this->rconn); - if (this->rxbuf) { + if (this->rxbuf && (i == HALF_REMOTE || !r->is_mgmt_conn)) { const struct hook *h; for (h = hooks; h < &hooks[n_hooks]; h++) { - if (h->packet_cb(r, i, h->aux)) { + if (h->packet_cb[i] && h->packet_cb[i](r, h->aux)) { - buffer_delete(this->rxbuf); + ofpbuf_delete(this->rxbuf); this->rxbuf = NULL; progress = true; break; @@@ -584,558 -497,6 +584,558 @@@ relay_destroy(struct relay *r free(r); } +/* Port status watcher. */ + +typedef void port_watcher_cb_func(uint16_t port_no, + const struct ofp_phy_port *old, + const struct ofp_phy_port *new, + void *aux); + +struct port_watcher_cb { + port_watcher_cb_func *function; + void *aux; +}; + +struct port_watcher { + struct rconn *local_rconn; + struct rconn *remote_rconn; + struct ofp_phy_port ports[OFPP_MAX + 1]; + time_t last_feature_request; + bool got_feature_reply; + int n_txq; + struct port_watcher_cb cbs[2]; + int n_cbs; +}; + +/* Returns the number of fields that differ from 'a' to 'b'. */ +static int +opp_differs(const struct ofp_phy_port *a, const struct ofp_phy_port *b) +{ + BUILD_ASSERT_DECL(sizeof *a == 36); /* Trips when we add or remove fields. */ + return ((a->port_no != b->port_no) + + (memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr) != 0) + + (memcmp(a->name, b->name, sizeof a->name) != 0) + + (a->flags != b->flags) + + (a->speed != b->speed) + + (a->features != b->features)); +} + +static void +sanitize_opp(struct ofp_phy_port *opp) +{ + size_t i; + + for (i = 0; i < sizeof opp->name; i++) { + char c = opp->name[i]; + if (c && (c < 0x20 || c > 0x7e)) { + opp->name[i] = '.'; + } + } + opp->name[sizeof opp->name - 1] = '\0'; +} + +static int +port_no_to_pw_idx(int port_no) +{ + return (port_no < OFPP_MAX ? port_no + : port_no == OFPP_LOCAL ? OFPP_MAX + : -1); +} + +static void +call_pw_callbacks(struct port_watcher *pw, int port_no, + const struct ofp_phy_port *old, + const struct ofp_phy_port *new) +{ + if (opp_differs(old, new)) { + int i; + for (i = 0; i < pw->n_cbs; i++) { + pw->cbs[i].function(port_no, old, new, pw->cbs[i].aux); + } + } +} + +static void +update_phy_port(struct port_watcher *pw, struct ofp_phy_port *opp, + uint8_t reason, bool seen[OFPP_MAX + 1]) +{ + struct ofp_phy_port *pw_opp; + struct ofp_phy_port old; + uint16_t port_no; + int idx; + + port_no = ntohs(opp->port_no); + idx = port_no_to_pw_idx(port_no); + if (idx < 0) { + return; + } + + if (seen) { + seen[idx] = true; + } + + pw_opp = &pw->ports[idx]; + old = *pw_opp; + if (reason == OFPPR_DELETE) { + memset(pw_opp, 0, sizeof *pw_opp); + pw_opp->port_no = htons(OFPP_NONE); + } else if (reason == OFPPR_MOD || reason == OFPPR_ADD) { + *pw_opp = *opp; + sanitize_opp(pw_opp); + } + call_pw_callbacks(pw, port_no, &old, pw_opp); +} + +static bool +port_watcher_local_packet_cb(struct relay *r, void *pw_) +{ + struct port_watcher *pw = pw_; - struct buffer *msg = r->halves[HALF_LOCAL].rxbuf; ++ struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf; + struct ofp_header *oh = msg->data; + + if (oh->type == OFPT_FEATURES_REPLY + && msg->size >= offsetof(struct ofp_switch_features, ports)) { + struct ofp_switch_features *osf = msg->data; + bool seen[ARRAY_SIZE(pw->ports)]; + size_t n_ports; + size_t i; + + pw->got_feature_reply = true; + + /* Update each port included in the message. */ + memset(seen, 0, sizeof seen); + n_ports = ((msg->size - offsetof(struct ofp_switch_features, ports)) + / sizeof *osf->ports); + for (i = 0; i < n_ports; i++) { + update_phy_port(pw, &osf->ports[i], OFPPR_MOD, seen); + } + + /* Delete all the ports not included in the message. */ + for (i = 0; i < ARRAY_SIZE(pw->ports); i++) { + if (!seen[i]) { + update_phy_port(pw, &pw->ports[i], OFPPR_DELETE, NULL); + } + } + } else if (oh->type == OFPT_PORT_STATUS + && msg->size >= sizeof(struct ofp_port_status)) { + struct ofp_port_status *ops = msg->data; + update_phy_port(pw, &ops->desc, ops->reason, NULL); + } + return false; +} + +static bool +port_watcher_remote_packet_cb(struct relay *r, void *pw_) +{ + struct port_watcher *pw = pw_; - struct buffer *msg = r->halves[HALF_REMOTE].rxbuf; ++ struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf; + struct ofp_header *oh = msg->data; + + if (oh->type == OFPT_PORT_MOD + && msg->size >= sizeof(struct ofp_port_mod)) { + struct ofp_port_mod *opm = msg->data; + uint16_t port_no = ntohs(opm->desc.port_no); + int idx = port_no_to_pw_idx(port_no); + if (idx >= 0) { + struct ofp_phy_port *pw_opp = &pw->ports[idx]; + if (pw_opp->port_no != htons(OFPP_NONE)) { + struct ofp_phy_port old = *pw_opp; + pw_opp->flags = ((pw_opp->flags & ~opm->mask) + | (opm->desc.flags & opm->mask)); + call_pw_callbacks(pw, port_no, &old, pw_opp); + } + } + } + return false; +} + +static void +port_watcher_periodic_cb(void *pw_) +{ + struct port_watcher *pw = pw_; + + if (!pw->got_feature_reply && time_now() >= pw->last_feature_request + 5) { - struct buffer *b; ++ struct ofpbuf *b; + make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &b); + rconn_send_with_limit(pw->local_rconn, b, &pw->n_txq, 1); + pw->last_feature_request = time_now(); + } +} + +static void +put_duplexes(struct ds *ds, const char *name, uint32_t features, + uint32_t hd_bit, uint32_t fd_bit) +{ + if (features & (hd_bit | fd_bit)) { + ds_put_format(ds, " %s", name); + if (features & hd_bit) { + ds_put_cstr(ds, "(HD)"); + } + if (features & fd_bit) { + ds_put_cstr(ds, "(FD)"); + } + } +} + +static void +log_port_status(uint16_t port_no, + const struct ofp_phy_port *old, + const struct ofp_phy_port *new, + void *aux) +{ + if (VLOG_IS_DBG_ENABLED()) { + bool was_enabled = old->port_no != htons(OFPP_NONE); + bool now_enabled = new->port_no != htons(OFPP_NONE); + uint32_t features = ntohl(new->features); + struct ds ds; + + if (old->flags != new->flags && opp_differs(old, new) == 1) { + /* Don't care if only flags changed. */ + return; + } + + ds_init(&ds); + ds_put_format(&ds, "\"%s\", "ETH_ADDR_FMT, new->name, + ETH_ADDR_ARGS(new->hw_addr)); + if (ntohl(new->speed)) { + ds_put_format(&ds, ", speed %"PRIu32, ntohl(new->speed)); + } + if (features & (OFPPF_10MB_HD | OFPPF_10MB_FD + | OFPPF_100MB_HD | OFPPF_100MB_FD + | OFPPF_1GB_HD | OFPPF_1GB_FD | OFPPF_10GB_FD)) { + ds_put_cstr(&ds, ", supports"); + put_duplexes(&ds, "10M", features, OFPPF_10MB_HD, OFPPF_10MB_FD); + put_duplexes(&ds, "100M", features, + OFPPF_100MB_HD, OFPPF_100MB_FD); + put_duplexes(&ds, "1G", features, OFPPF_100MB_HD, OFPPF_100MB_FD); + if (features & OFPPF_10GB_FD) { + ds_put_cstr(&ds, " 10G"); + } + } + if (was_enabled != now_enabled) { + if (now_enabled) { + VLOG_DBG("Port %d added: %s", port_no, ds_cstr(&ds)); + } else { + VLOG_DBG("Port %d deleted", port_no); + } + } else { + VLOG_DBG("Port %d changed: %s", port_no, ds_cstr(&ds)); + } + ds_destroy(&ds); + } +} + +static void +port_watcher_register_callback(struct port_watcher *pw, + port_watcher_cb_func *function, + void *aux) +{ + assert(pw->n_cbs < ARRAY_SIZE(pw->cbs)); + pw->cbs[pw->n_cbs].function = function; + pw->cbs[pw->n_cbs].aux = aux; + pw->n_cbs++; +} + +static uint32_t +port_watcher_get_flags(const struct port_watcher *pw, int port_no) +{ + int idx = port_no_to_pw_idx(port_no); + return idx >= 0 ? ntohl(pw->ports[idx].flags) : 0; +} + +static void +port_watcher_set_flags(struct port_watcher *pw, + int port_no, uint32_t flags, uint32_t mask) +{ + struct ofp_phy_port old; + struct ofp_phy_port *p; + struct ofp_port_mod *opm; + struct ofp_port_status *ops; - struct buffer *b; ++ struct ofpbuf *b; + int idx; + + idx = port_no_to_pw_idx(port_no); + if (idx < 0) { + return; + } + + p = &pw->ports[idx]; + if (!((ntohl(p->flags) ^ flags) & mask)) { + return; + } + old = *p; + + /* Update our idea of the flags. */ + p->flags = htonl((ntohl(p->flags) & ~mask) | (flags & mask)); + call_pw_callbacks(pw, port_no, &old, p); + + /* Change the flags in the datapath. */ + opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b); + opm->mask = htonl(mask); + opm->desc = *p; + rconn_send(pw->local_rconn, b, NULL); + + /* Notify the controller that the flags changed. */ + ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b); + ops->reason = OFPPR_MOD; + ops->desc = *p; + rconn_send(pw->remote_rconn, b, NULL); +} + +static bool +port_watcher_is_ready(const struct port_watcher *pw) +{ + return pw->got_feature_reply; +} + +static struct hook +port_watcher_create(struct rconn *local_rconn, struct rconn *remote_rconn, + struct port_watcher **pwp) +{ + struct port_watcher *pw; + int i; + + pw = *pwp = xcalloc(1, sizeof *pw); + pw->local_rconn = local_rconn; + pw->remote_rconn = remote_rconn; + pw->last_feature_request = TIME_MIN; + for (i = 0; i < OFPP_MAX; i++) { + pw->ports[i].port_no = htons(OFPP_NONE); + } + port_watcher_register_callback(pw, log_port_status, NULL); + return make_hook(port_watcher_local_packet_cb, + port_watcher_remote_packet_cb, + port_watcher_periodic_cb, NULL, pw); +} + +/* Spanning tree protocol. */ + +/* Extra time, in seconds, at boot before going into fail-open, to give the + * spanning tree protocol time to figure out the network layout. */ +#define STP_EXTRA_BOOT_TIME 30 + +struct stp_data { + struct stp *stp; + struct port_watcher *pw; + struct rconn *local_rconn; + struct rconn *remote_rconn; + uint8_t dpid[ETH_ADDR_LEN]; + long long int last_tick_256ths; + int n_txq; +}; + +static bool +stp_local_packet_cb(struct relay *r, void *stp_) +{ + struct stp_data *stp = stp_; + struct ofp_packet_in *opi; + struct eth_header *eth; + struct llc_header *llc; - struct buffer payload; ++ struct ofpbuf payload; + uint16_t port_no; + struct flow flow; + + if (!get_ofp_packet_eth_header(r, &opi, ð) + || !eth_addr_equals(eth->eth_dst, stp_eth_addr)) { + return false; + } + + port_no = ntohs(opi->in_port); + if (port_no >= STP_MAX_PORTS) { + /* STP only supports 255 ports. */ + return false; + } + if (port_watcher_get_flags(stp->pw, port_no) & OFPPFL_NO_STP) { + /* We're not doing STP on this port. */ + return false; + } + + if (opi->reason == OFPR_ACTION) { + /* The controller set up a flow for this, so we won't intercept it. */ + return false; + } + + get_ofp_packet_payload(opi, &payload); + flow_extract(&payload, port_no, &flow); + if (flow.dl_type != htons(OFP_DL_TYPE_NOT_ETH_TYPE)) { + VLOG_DBG("non-LLC frame received on STP multicast address"); + return false; + } - llc = buffer_at_assert(&payload, sizeof *eth, sizeof *llc); ++ llc = ofpbuf_at_assert(&payload, sizeof *eth, sizeof *llc); + if (llc->llc_dsap != STP_LLC_DSAP) { + VLOG_DBG("bad DSAP 0x%02"PRIx8" received on STP multicast address", + llc->llc_dsap); + return false; + } + + /* Trim off padding on payload. */ + if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) { + payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN; + } - if (buffer_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) { ++ if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) { + struct stp_port *p = stp_get_port(stp->stp, port_no); + stp_received_bpdu(p, payload.data, payload.size); + } + + return true; +} + +static long long int +time_256ths(void) +{ + return time_msec() * 256 / 1000; +} + +static void +stp_periodic_cb(void *stp_) +{ + struct stp_data *stp = stp_; + long long int now_256ths = time_256ths(); + long long int elapsed_256ths = now_256ths - stp->last_tick_256ths; + struct stp_port *p; + + if (!port_watcher_is_ready(stp->pw)) { + /* Can't start STP until we know port flags, because port flags can + * disable STP. */ + return; + } + if (elapsed_256ths <= 0) { + return; + } + + stp_tick(stp->stp, MIN(INT_MAX, elapsed_256ths)); + stp->last_tick_256ths = now_256ths; + + while (stp_get_changed_port(stp->stp, &p)) { + int port_no = stp_port_no(p); + enum stp_state state = stp_port_get_state(p); + + if (state != STP_DISABLED) { + VLOG_WARN("STP: Port %d entered %s state", + port_no, stp_state_name(state)); + } + if (!(port_watcher_get_flags(stp->pw, port_no) & OFPPFL_NO_STP)) { + uint32_t flags; + switch (state) { + case STP_LISTENING: + flags = OFPPFL_STP_LISTEN; + break; + case STP_LEARNING: + flags = OFPPFL_STP_LEARN; + break; + case STP_DISABLED: + case STP_FORWARDING: + flags = OFPPFL_STP_FORWARD; + break; + case STP_BLOCKING: + flags = OFPPFL_STP_BLOCK; + break; + default: + VLOG_DBG_RL(&vrl, "STP: Port %d has bad state %x", + port_no, state); + flags = OFPPFL_STP_FORWARD; + break; + } + if (!stp_forward_in_state(state)) { + flags |= OFPPFL_NO_FLOOD; + } + port_watcher_set_flags(stp->pw, port_no, flags, + OFPPFL_STP_MASK | OFPPFL_NO_FLOOD); + } else { + /* We don't own those flags. */ + } + } +} + +static void +stp_wait_cb(void *stp_ UNUSED) +{ + poll_timer_wait(1000); +} + +static void +send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_) +{ + struct stp_data *stp = stp_; + struct eth_header *eth; + struct llc_header *llc; - struct buffer pkt, *opo; ++ struct ofpbuf pkt, *opo; + + /* Packet skeleton. */ - buffer_init(&pkt, ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size); - eth = buffer_put_uninit(&pkt, sizeof *eth); - llc = buffer_put_uninit(&pkt, sizeof *llc); - buffer_put(&pkt, bpdu, bpdu_size); ++ ofpbuf_init(&pkt, ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size); ++ eth = ofpbuf_put_uninit(&pkt, sizeof *eth); ++ llc = ofpbuf_put_uninit(&pkt, sizeof *llc); ++ ofpbuf_put(&pkt, bpdu, bpdu_size); + + /* 802.2 header. */ + memcpy(eth->eth_dst, stp_eth_addr, ETH_ADDR_LEN); + memcpy(eth->eth_src, stp->pw->ports[port_no].hw_addr, ETH_ADDR_LEN); + eth->eth_type = htons(pkt.size - ETH_HEADER_LEN); + + /* LLC header. */ + llc->llc_dsap = STP_LLC_DSAP; + llc->llc_ssap = STP_LLC_SSAP; + llc->llc_cntl = STP_LLC_CNTL; + + opo = make_unbuffered_packet_out(&pkt, OFPP_NONE, port_no); - buffer_uninit(&pkt); ++ ofpbuf_uninit(&pkt); + rconn_send_with_limit(stp->local_rconn, opo, &stp->n_txq, OFPP_MAX); +} + +static void +stp_port_watcher_cb(uint16_t port_no, + const struct ofp_phy_port *old, + const struct ofp_phy_port *new, + void *stp_) +{ + struct stp_data *stp = stp_; + struct stp_port *p; + + /* STP only supports a maximum of 255 ports, one less than OpenFlow. We + * don't support STP on OFPP_LOCAL, either. */ + if (port_no >= STP_MAX_PORTS) { + return; + } + + p = stp_get_port(stp->stp, port_no); + if (new->port_no == htons(OFPP_NONE) + || new->flags & htonl(OFPPFL_NO_STP)) { + stp_port_disable(p); + } else { + stp_port_enable(p); + stp_port_set_speed(p, new->speed); + } +} + +static struct hook +stp_hook_create(const struct settings *s, struct port_watcher *pw, + struct rconn *local, struct rconn *remote) +{ + uint8_t dpid[ETH_ADDR_LEN]; + struct netdev *netdev; + struct stp_data *stp; + int retval; + + retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev); + if (retval) { - fatal(retval, "Could not open %s device", s->of_name); ++ ofp_fatal(retval, "Could not open %s device", s->of_name); + } + memcpy(dpid, netdev_get_etheraddr(netdev), ETH_ADDR_LEN); + netdev_close(netdev); + + stp = xcalloc(1, sizeof *stp); + stp->stp = stp_create("stp", eth_addr_to_uint64(dpid), send_bpdu, stp); + stp->pw = pw; + memcpy(stp->dpid, dpid, ETH_ADDR_LEN); + stp->local_rconn = local; + stp->remote_rconn = remote; + stp->last_tick_256ths = time_256ths(); + + port_watcher_register_callback(pw, stp_port_watcher_cb, stp); + return make_hook(stp_local_packet_cb, NULL, + stp_periodic_cb, stp_wait_cb, stp); +} + /* In-band control. */ struct in_band_data { @@@ -1225,56 -576,74 +1225,56 @@@ in_band_local_packet_cb(struct relay *r { struct in_band_data *in_band = in_band_; struct rconn *rc = r->halves[HALF_LOCAL].rconn; - struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf; struct ofp_packet_in *opi; - struct ofp_header *oh; - size_t pkt_ofs, pkt_len; - struct ofpbuf pkt; + struct eth_header *eth; - struct buffer payload; ++ struct ofpbuf payload; struct flow flow; - uint16_t in_port, out_port; - const uint8_t *controller_mac; + uint16_t in_port; + int out_port; - if (half != HALF_LOCAL || r->is_mgmt_conn) { + if (!get_ofp_packet_eth_header(r, &opi, ð)) { return false; } - - oh = msg->data; - if (oh->type != OFPT_PACKET_IN) { - return false; - } - if (msg->size < offsetof(struct ofp_packet_in, data)) { - VLOG_WARN_RL(&vrl, "packet too short (%zu bytes) for packet_in", - msg->size); - return false; - } - - /* Extract flow data from 'opi' into 'flow'. */ - opi = msg->data; in_port = ntohs(opi->in_port); - pkt_ofs = offsetof(struct ofp_packet_in, data); - pkt_len = ntohs(opi->header.length) - pkt_ofs; - pkt.data = opi->data; - pkt.size = pkt_len; - flow_extract(&pkt, in_port, &flow); /* Deal with local stuff. */ - controller_mac = get_controller_mac(in_band); if (in_port == OFPP_LOCAL) { /* Sent by secure channel. */ - out_port = mac_learning_lookup(in_band->ml, flow.dl_dst); - } else if (eth_addr_equals(flow.dl_dst, in_band->mac)) { + out_port = mac_learning_lookup(in_band->ml, eth->eth_dst); + } else if (eth_addr_equals(eth->eth_dst, in_band->mac)) { /* Sent to secure channel. */ out_port = OFPP_LOCAL; - if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) { - VLOG_DBG_RL(&vrl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16, - ETH_ADDR_ARGS(flow.dl_src), in_port); - } - } else if (flow.dl_type == htons(ETH_TYPE_ARP) - && eth_addr_is_broadcast(flow.dl_dst) - && is_controller_mac(flow.dl_src, in_band)) { + in_band_learn_mac(in_band, in_port, eth->eth_src); + } else if (eth->eth_type == htons(ETH_TYPE_ARP) + && eth_addr_is_broadcast(eth->eth_dst) + && is_controller_mac(eth->eth_src, in_band)) { /* ARP sent by controller. */ out_port = OFPP_FLOOD; - } else if (is_controller_mac(flow.dl_dst, in_band)) { - if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) { - VLOG_DBG_RL(&vrl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16, - ETH_ADDR_ARGS(flow.dl_src), in_port); - } - - out_port = mac_learning_lookup(in_band->ml, controller_mac); - if (in_port != out_port) { + } else if (is_controller_mac(eth->eth_dst, in_band) + || is_controller_mac(eth->eth_src, in_band)) { + /* Traffic to or from controller. Switch it by hand. */ + in_band_learn_mac(in_band, in_port, eth->eth_src); + out_port = mac_learning_lookup(in_band->ml, eth->eth_dst); + } else { + const uint8_t *controller_mac; + controller_mac = get_controller_mac(in_band); + if (eth->eth_type == htons(ETH_TYPE_ARP) + && eth_addr_is_broadcast(eth->eth_dst) + && is_controller_mac(eth->eth_src, in_band)) { + /* ARP sent by controller. */ + out_port = OFPP_FLOOD; + } else if (is_controller_mac(eth->eth_dst, in_band) + && in_port == mac_learning_lookup(in_band->ml, + controller_mac)) { + /* Drop controller traffic that arrives on the controller port. */ + out_port = -1; + } else { return false; } - - /* This is controller traffic that arrived on the controller port. - * It will get dropped below. */ - } else if (is_controller_mac(flow.dl_src, in_band)) { - out_port = mac_learning_lookup(in_band->ml, flow.dl_dst); - } else { - return false; } + get_ofp_packet_payload(opi, &payload); + flow_extract(&payload, in_port, &flow); if (in_port == out_port) { /* The input and output port match. Set up a flow to drop packets. */ queue_tx(rc, in_band, make_add_flow(&flow, ntohl(opi->buffer_id), @@@ -1293,9 -662,9 +1293,9 @@@ } else { /* We don't know that MAC. Send along the packet without setting up a * flow. */ - struct buffer *b; + struct ofpbuf *b; if (ntohl(opi->buffer_id) == UINT32_MAX) { - b = make_unbuffered_packet_out(&pkt, in_port, out_port); + b = make_unbuffered_packet_out(&payload, in_port, out_port); } else { b = make_buffered_packet_out(ntohl(opi->buffer_id), in_port, out_port); @@@ -1331,14 -700,6 +1331,14 @@@ in_band_status_cb(struct status_reply * } } +static void - get_ofp_packet_payload(struct ofp_packet_in *opi, struct buffer *payload) ++get_ofp_packet_payload(struct ofp_packet_in *opi, struct ofpbuf *payload) +{ + payload->data = opi->data; + payload->size = ntohs(opi->header.length) - offsetof(struct ofp_packet_in, + data); +} + static struct hook in_band_hook_create(const struct settings *s, struct switch_status *ss, struct rconn *remote) @@@ -1572,7 -937,7 +1572,7 @@@ rate_limit_local_packet_cb(struct rela return false; } else { /* Otherwise queue it up for the periodic callback to drain out. */ - struct buffer *msg = r->halves[HALF_LOCAL].rxbuf; - struct ofp_packet_in *opi = msg->data; ++ struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf; int port = ntohs(opi->in_port) % OFPP_MAX; if (rl->n_queued >= s->burst_limit) { drop_packet(rl); @@@ -1679,21 -1044,31 +1679,21 @@@ switch_status_remote_packet_cb(struct r { struct switch_status *ss = ss_; struct rconn *rc = r->halves[HALF_REMOTE].rconn; - struct buffer *msg = r->halves[HALF_REMOTE].rxbuf; + struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf; struct switch_status_category *c; - struct ofp_stats_request *osr; - struct ofp_stats_reply *reply; + struct nicira_header *request; + struct nicira_header *reply; struct status_reply sr; - struct buffer *b; - struct ofp_header *oh; + struct ofpbuf *b; int retval; - if (half == HALF_LOCAL) { + if (msg->size < sizeof(struct nicira_header)) { return false; } - - oh = msg->data; - if (oh->type != OFPT_STATS_REQUEST) { - return false; - } - if (msg->size < sizeof(struct ofp_stats_request)) { - VLOG_WARN_RL(&vrl, "packet too short (%zu bytes) for stats_request", - msg->size); - return false; - } - - osr = msg->data; - if (osr->type != htons(OFPST_SWITCH)) { + request = msg->data; + if (request->header.type != OFPT_VENDOR + || request->vendor_id != htonl(NX_VENDOR_ID) + || request->subtype != htonl(NXT_STATUS_REQUEST)) { return false; } @@@ -2123,13 -1498,6 +2124,13 @@@ parse_options(int argc, char *argv[], s s->listener_names[s->n_listeners++] = optarg; break; + case 'm': + if (s->monitor_name) { - fatal(0, "-m or --monitor may only be specified once"); ++ ofp_fatal(0, "-m or --monitor may only be specified once"); + } + s->monitor_name = optarg; + break; + case 'h': usage(); diff --cc switch/datapath.c index 7ed4f2d8,5eeefa78..0e715f17 --- a/switch/datapath.c +++ b/switch/datapath.c @@@ -46,10 -48,10 +48,11 @@@ #include "packets.h" #include "poll-loop.h" #include "rconn.h" +#include "stp.h" - #include "vconn.h" + #include "switch-flow.h" #include "table.h" #include "timeval.h" + #include "vconn.h" #include "xtoxll.h" #define THIS_MODULE VLM_datapath @@@ -142,25 -150,24 +145,25 @@@ static void remote_run(struct datapath static void remote_wait(struct remote *); static void remote_destroy(struct remote *); - void dp_output_port(struct datapath *, struct buffer *, + void dp_output_port(struct datapath *, struct ofpbuf *, - int in_port, int out_port); + int in_port, int out_port, bool ignore_no_fwd); void dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm); - void dp_output_control(struct datapath *, struct buffer *, int in_port, + void dp_output_control(struct datapath *, struct ofpbuf *, int in_port, size_t max_len, int reason); static void send_flow_expired(struct datapath *, struct sw_flow *, enum ofp_flow_expired_reason); static int update_port_status(struct sw_port *p); static void send_port_status(struct sw_port *p, uint8_t status); static void del_switch_port(struct sw_port *p); - static void execute_actions(struct datapath *, struct buffer *, + static void execute_actions(struct datapath *, struct ofpbuf *, int in_port, const struct sw_flow_key *, - const struct ofp_action *, int n_actions); + const struct ofp_action *, int n_actions, + bool ignore_no_fwd); - static void modify_vlan(struct buffer *buffer, const struct sw_flow_key *key, + static void modify_vlan(struct ofpbuf *buffer, const struct sw_flow_key *key, const struct ofp_action *a); - static void modify_nh(struct buffer *buffer, uint16_t eth_proto, + static void modify_nh(struct ofpbuf *buffer, uint16_t eth_proto, uint8_t nw_proto, const struct ofp_action *a); - static void modify_th(struct buffer *buffer, uint16_t eth_proto, + static void modify_th(struct ofpbuf *buffer, uint16_t eth_proto, uint8_t nw_proto, const struct ofp_action *a); /* Buffers are identified to userspace by a 31-bit opaque ID. We divide the ID @@@ -174,9 -181,8 +177,9 @@@ #define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS) - int run_flow_through_tables(struct datapath *, struct buffer *, -int run_flow_through_tables(struct datapath *, struct ofpbuf *, int in_port); -void fwd_port_input(struct datapath *, struct ofpbuf *, int in_port); ++int run_flow_through_tables(struct datapath *, struct ofpbuf *, + struct sw_port *); - void fwd_port_input(struct datapath *, struct buffer *, struct sw_port *); ++void fwd_port_input(struct datapath *, struct ofpbuf *, struct sw_port *); int fwd_control_input(struct datapath *, const struct sender *, const void *, size_t); @@@ -528,15 -534,14 +531,15 @@@ output_all(struct datapath *dp, struct continue; } if (prev_port != -1) { - dp_output_port(dp, buffer_clone(buffer), in_port, prev_port, - dp_output_port(dp, ofpbuf_clone(buffer), in_port, prev_port); ++ dp_output_port(dp, ofpbuf_clone(buffer), in_port, prev_port, + false); } prev_port = port_no(dp, p); } if (prev_port != -1) - dp_output_port(dp, buffer, in_port, prev_port); + dp_output_port(dp, buffer, in_port, prev_port, false); else - buffer_delete(buffer); + ofpbuf_delete(buffer); return 0; } @@@ -564,8 -569,8 +567,8 @@@ output_packet(struct datapath *dp, stru /* Takes ownership of 'buffer' and transmits it to 'out_port' on 'dp'. */ void - dp_output_port(struct datapath *dp, struct buffer *buffer, + dp_output_port(struct datapath *dp, struct ofpbuf *buffer, - int in_port, int out_port) + int in_port, int out_port, bool ignore_no_fwd) { assert(buffer); @@@ -578,9 -583,8 +581,9 @@@ } else if (out_port == OFPP_IN_PORT) { output_packet(dp, buffer, in_port); } else if (out_port == OFPP_TABLE) { - if (run_flow_through_tables(dp, buffer, in_port)) { + struct sw_port *p = in_port < OFPP_MAX ? &dp->ports[in_port] : 0; + if (run_flow_through_tables(dp, buffer, p)) { - buffer_delete(buffer); + ofpbuf_delete(buffer); } } else { if (in_port == out_port) { @@@ -672,13 -683,16 +675,13 @@@ dp_send_features_reply(struct datapath ofr = make_openflow_reply(sizeof *ofr, OFPT_FEATURES_REPLY, sender, &buffer); - ofr->datapath_id = htonll(dp->id); - ofr->n_exact = htonl(2 * TABLE_HASH_MAX_FLOWS); - ofr->n_compression = 0; /* Not supported */ - ofr->n_general = htonl(TABLE_LINEAR_MAX_FLOWS); - ofr->buffer_mb = htonl(UINT32_MAX); - ofr->n_buffers = htonl(N_PKT_BUFFERS); - ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); - ofr->actions = htonl(OFP_SUPPORTED_ACTIONS); + ofr->datapath_id = htonll(dp->id); + ofr->n_tables = dp->chain->n_tables; + ofr->n_buffers = htonl(N_PKT_BUFFERS); + ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); + ofr->actions = htonl(OFP_SUPPORTED_ACTIONS); LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { - struct ofp_phy_port *opp = buffer_put_uninit(buffer, sizeof *opp); + struct ofp_phy_port *opp = ofpbuf_put_uninit(buffer, sizeof *opp); memset(opp, 0, sizeof *opp); fill_port_desc(dp, p, opp); } @@@ -793,11 -807,12 +796,11 @@@ send_flow_expired(struct datapath *dp, void dp_send_error_msg(struct datapath *dp, const struct sender *sender, - uint16_t type, uint16_t code, const uint8_t *data, size_t len) + uint16_t type, uint16_t code, const void *data, size_t len) { - struct buffer *buffer; + struct ofpbuf *buffer; struct ofp_error_msg *oem; - oem = make_openflow_reply(sizeof(*oem)+len, OFPT_ERROR_MSG, - sender, &buffer); + oem = make_openflow_reply(sizeof(*oem)+len, OFPT_ERROR, sender, &buffer); oem->type = htons(type); oem->code = htons(code); memcpy(oem->data, data, len); @@@ -838,29 -853,23 +841,29 @@@ fill_flow_stats(struct ofpbuf *buffer, } -/* 'buffer' was received on 'in_port', a physical switch port between 0 and - * OFPP_MAX. Process it according to 'dp''s flow table. Returns 0 if +/* 'buffer' was received on 'p', which may be a a physical switch port or a + * null pointer. Process it according to 'dp''s flow table. Returns 0 if * successful, in which case 'buffer' is destroyed, or -ESRCH if there is no * matching flow, in which case 'buffer' still belongs to the caller. */ - int run_flow_through_tables(struct datapath *dp, struct buffer *buffer, + int run_flow_through_tables(struct datapath *dp, struct ofpbuf *buffer, - int in_port) + struct sw_port *p) { struct sw_flow_key key; struct sw_flow *flow; key.wildcards = 0; - if (flow_extract(buffer, in_port, &key.flow) + if (flow_extract(buffer, p ? port_no(dp, p) : OFPP_NONE, &key.flow) && (dp->flags & OFPC_FRAG_MASK) == OFPC_FRAG_DROP) { /* Drop fragment. */ - buffer_delete(buffer); + ofpbuf_delete(buffer); return 0; } + if (p && p->flags & (OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP) + && p->flags & (!eth_addr_equals(key.flow.dl_dst, stp_eth_addr) + ? OFPPFL_NO_RECV : OFPPFL_NO_RECV_STP)) { - buffer_delete(buffer); ++ ofpbuf_delete(buffer); + return 0; + } flow = chain_lookup(dp->chain, &key); if (flow != NULL) { @@@ -873,34 -882,32 +876,34 @@@ } } -/* 'buffer' was received on 'in_port', a physical switch port between 0 and - * OFPP_MAX. Process it according to 'dp''s flow table, sending it up to the - * controller if no flow matches. Takes ownership of 'buffer'. */ -void fwd_port_input(struct datapath *dp, struct ofpbuf *buffer, int in_port) +/* 'buffer' was received on 'p', which may be a a physical switch port or a + * null pointer. Process it according to 'dp''s flow table, sending it up to + * the controller if no flow matches. Takes ownership of 'buffer'. */ - void fwd_port_input(struct datapath *dp, struct buffer *buffer, ++void fwd_port_input(struct datapath *dp, struct ofpbuf *buffer, + struct sw_port *p) { - if (run_flow_through_tables(dp, buffer, in_port)) { - dp_output_control(dp, buffer, in_port, dp->miss_send_len, - OFPR_NO_MATCH); + if (run_flow_through_tables(dp, buffer, p)) { + dp_output_control(dp, buffer, port_no(dp, p), + dp->miss_send_len, OFPR_NO_MATCH); } } static void - do_output(struct datapath *dp, struct buffer *buffer, int in_port, + do_output(struct datapath *dp, struct ofpbuf *buffer, int in_port, - size_t max_len, int out_port) + size_t max_len, int out_port, bool ignore_no_fwd) { if (out_port != OFPP_CONTROLLER) { - dp_output_port(dp, buffer, in_port, out_port); + dp_output_port(dp, buffer, in_port, out_port, ignore_no_fwd); } else { dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION); } } static void - execute_actions(struct datapath *dp, struct buffer *buffer, + execute_actions(struct datapath *dp, struct ofpbuf *buffer, int in_port, const struct sw_flow_key *key, - const struct ofp_action *actions, int n_actions) + const struct ofp_action *actions, int n_actions, + bool ignore_no_fwd) { /* Every output action needs a separate clone of 'buffer', but the common * case is just a single output action, so that doing a clone and then @@@ -919,8 -926,7 +922,8 @@@ struct eth_header *eh = buffer->l2; if (prev_port != -1) { - do_output(dp, buffer_clone(buffer), in_port, max_len, prev_port, - do_output(dp, ofpbuf_clone(buffer), in_port, max_len, prev_port); ++ do_output(dp, ofpbuf_clone(buffer), in_port, max_len, prev_port, + ignore_no_fwd); prev_port = -1; } @@@ -957,12 -963,12 +960,12 @@@ } } if (prev_port != -1) - do_output(dp, buffer, in_port, max_len, prev_port); + do_output(dp, buffer, in_port, max_len, prev_port, ignore_no_fwd); else - buffer_delete(buffer); + ofpbuf_delete(buffer); } - static void modify_nh(struct buffer *buffer, uint16_t eth_proto, + static void modify_nh(struct ofpbuf *buffer, uint16_t eth_proto, uint8_t nw_proto, const struct ofp_action *a) { if (eth_proto == ETH_TYPE_IP) { @@@ -1634,67 -1650,63 +1649,65 @@@ in fwd_control_input(struct datapath *dp, const struct sender *sender, const void *msg, size_t length) { - struct openflow_packet { - size_t min_size; - int (*handler)(struct datapath *, const struct sender *, const void *); - }; - - static const struct openflow_packet packets[] = { - [OFPT_FEATURES_REQUEST] = { - sizeof (struct ofp_header), - recv_features_request, - }, - [OFPT_GET_CONFIG_REQUEST] = { - sizeof (struct ofp_header), - recv_get_config_request, - }, - [OFPT_SET_CONFIG] = { - sizeof (struct ofp_switch_config), - recv_set_config, - }, - [OFPT_PACKET_OUT] = { - sizeof (struct ofp_packet_out), - recv_packet_out, - }, - [OFPT_FLOW_MOD] = { - sizeof (struct ofp_flow_mod), - recv_flow, - }, - [OFPT_PORT_MOD] = { - sizeof (struct ofp_port_mod), - recv_port_mod, - }, - [OFPT_STATS_REQUEST] = { - sizeof (struct ofp_stats_request), - recv_stats_request, - }, - [OFPT_ECHO_REQUEST] = { - sizeof (struct ofp_header), - recv_echo_request, - }, - [OFPT_ECHO_REPLY] = { - sizeof (struct ofp_header), - recv_echo_reply, - }, - }; - + int (*handler)(struct datapath *, const struct sender *, const void *); struct ofp_header *oh; + size_t min_size; + /* Check encapsulated length. */ oh = (struct ofp_header *) msg; - if (ntohs(oh->length) > length) + if (ntohs(oh->length) > length) { + return -EINVAL; + } + assert(oh->version == OFP_VERSION); + + /* Figure out how to handle it. */ + switch (oh->type) { + case OFPT_FEATURES_REQUEST: + min_size = sizeof(struct ofp_header); + handler = recv_features_request; + break; + case OFPT_GET_CONFIG_REQUEST: + min_size = sizeof(struct ofp_header); + handler = recv_get_config_request; + break; + case OFPT_SET_CONFIG: + min_size = sizeof(struct ofp_switch_config); + handler = recv_set_config; + break; + case OFPT_PACKET_OUT: + min_size = sizeof(struct ofp_packet_out); + handler = recv_packet_out; + break; + case OFPT_FLOW_MOD: + min_size = sizeof(struct ofp_flow_mod); + handler = recv_flow; + break; + case OFPT_PORT_MOD: + min_size = sizeof(struct ofp_port_mod); + handler = recv_port_mod; + break; + case OFPT_STATS_REQUEST: + min_size = sizeof(struct ofp_stats_request); + handler = recv_stats_request; + break; + case OFPT_ECHO_REQUEST: + min_size = sizeof(struct ofp_header); + handler = recv_echo_request; + break; + case OFPT_ECHO_REPLY: + min_size = sizeof(struct ofp_header); + handler = recv_echo_reply; + break; + default: - return -ENOSYS; ++ dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE, ++ msg, length); + return -EINVAL; - - if (oh->type < ARRAY_SIZE(packets)) { - const struct openflow_packet *pkt = &packets[oh->type]; - if (pkt->handler) { - if (length < pkt->min_size) - return -EFAULT; - return pkt->handler(dp, sender, msg); - } } - dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE, - msg, length); - return -EINVAL; + + /* Handle it. */ + if (length < min_size) + return -EFAULT; + return handler(dp, sender, msg); } /* Packet buffering. */ diff --cc switch/switch.c index 1f85b37b,cd14f051..4c750613 --- a/switch/switch.c +++ b/switch/switch.c @@@ -99,21 -99,24 +99,21 @@@ main(int argc, char *argv[] rconn = rconn_create(60, max_backoff); error = rconn_connect(rconn, argv[optind]); if (error == EAFNOSUPPORT) { - fatal(0, "no support for %s vconn", argv[optind]); + ofp_fatal(0, "no support for %s vconn", argv[optind]); } error = dp_new(&dp, dpid, rconn); - if (listen_vconn_name) { - struct vconn *listen_vconn; + if (listen_pvconn_name) { + struct pvconn *listen_pvconn; int retval; - - retval = vconn_open(listen_vconn_name, &listen_vconn); + + retval = pvconn_open(listen_pvconn_name, &listen_pvconn); if (retval && retval != EAGAIN) { - fatal(retval, "opening %s", listen_pvconn_name); - ofp_fatal(retval, "opening %s", listen_vconn_name); - } - if (!vconn_is_passive(listen_vconn)) { - ofp_fatal(0, "%s is not a passive vconn", listen_vconn_name); ++ ofp_fatal(retval, "opening %s", listen_pvconn_name); } - dp_add_listen_vconn(dp, listen_vconn); + dp_add_listen_pvconn(dp, listen_pvconn); } if (error) { - fatal(error, "could not create datapath"); + ofp_fatal(error, "could not create datapath"); } if (port_list) { add_ports(dp, port_list); @@@ -264,10 -268,10 +265,10 @@@ parse_options(int argc, char *argv[] break; case 'l': - if (listen_vconn_name) { + if (listen_pvconn_name) { - fatal(0, "-l or --listen may be only specified once"); + ofp_fatal(0, "-l or --listen may be only specified once"); } - listen_vconn_name = optarg; + listen_pvconn_name = optarg; break; VCONN_SSL_OPTION_HANDLERS diff --cc switch/table-linear.c index 355bda27,ea3777f2..18945778 --- a/switch/table-linear.c +++ b/switch/table-linear.c @@@ -36,6 -36,6 +36,7 @@@ #include #include "flow.h" #include "list.h" ++#include "openflow.h" #include "switch-flow.h" #include "datapath.h" diff --cc tests/test-stp.c index 237e7d56,00000000..9b87c0e6 mode 100644,000000..100644 --- a/tests/test-stp.c +++ b/tests/test-stp.c @@@ -1,661 -1,0 +1,661 @@@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#include "stp.h" +#include +#include +#include +#include +#include +#include +#include "packets.h" + +struct bpdu { + int port_no; + void *data; + size_t size; +}; + +struct bridge { + struct test_case *tc; + int id; + bool reached; + + struct stp *stp; + + struct lan *ports[STP_MAX_PORTS]; + int n_ports; + +#define RXQ_SIZE 16 + struct bpdu rxq[RXQ_SIZE]; + int rxq_head, rxq_tail; +}; + +struct lan_conn { + struct bridge *bridge; + int port_no; +}; + +struct lan { + struct test_case *tc; + const char *name; + bool reached; + struct lan_conn conns[16]; + int n_conns; +}; + +struct test_case { + struct bridge *bridges[16]; + int n_bridges; + struct lan *lans[26]; + int n_lans; +}; + +static const char *file_name; +static int line_number; +static char line[128]; +static char *pos, *token; +static int n_warnings; + +static struct test_case * +new_test_case(void) +{ + struct test_case *tc = xmalloc(sizeof *tc); + tc->n_bridges = 0; + tc->n_lans = 0; + return tc; +} + +static void +send_bpdu(const void *data, size_t size, int port_no, void *b_) +{ + struct bridge *b = b_; + struct lan *lan; + int i; + + assert(port_no < b->n_ports); + lan = b->ports[port_no]; + if (!lan) { + return; + } + for (i = 0; i < lan->n_conns; i++) { + struct lan_conn *conn = &lan->conns[i]; + if (conn->bridge != b || conn->port_no != port_no) { + struct bridge *dst = conn->bridge; + struct bpdu *bpdu = &dst->rxq[dst->rxq_head++ % RXQ_SIZE]; + assert(dst->rxq_head - dst->rxq_tail <= RXQ_SIZE); + bpdu->data = xmemdup(data, size); + bpdu->size = size; + bpdu->port_no = conn->port_no; + } + } +} + +static struct bridge * +new_bridge(struct test_case *tc, int id) +{ + struct bridge *b = xmalloc(sizeof *b); + char name[16]; + b->tc = tc; + b->id = id; + snprintf(name, sizeof name, "stp%x", id); + b->stp = stp_create(name, id, send_bpdu, b); + assert(tc->n_bridges < ARRAY_SIZE(tc->bridges)); + b->n_ports = 0; + b->rxq_head = b->rxq_tail = 0; + tc->bridges[tc->n_bridges++] = b; + return b; +} + +static struct lan * +new_lan(struct test_case *tc, const char *name) +{ + struct lan *lan = xmalloc(sizeof *lan); + lan->tc = tc; + lan->name = xstrdup(name); + lan->n_conns = 0; + assert(tc->n_lans < ARRAY_SIZE(tc->lans)); + tc->lans[tc->n_lans++] = lan; + return lan; +} + +static void +reconnect_port(struct bridge *b, int port_no, struct lan *new_lan) +{ + struct lan *old_lan; + int j; + + assert(port_no < b->n_ports); + old_lan = b->ports[port_no]; + if (old_lan == new_lan) { + return; + } + + /* Disconnect from old_lan. */ + if (old_lan) { + for (j = 0; j < old_lan->n_conns; j++) { + struct lan_conn *c = &old_lan->conns[j]; + if (c->bridge == b && c->port_no == port_no) { + memmove(c, c + 1, sizeof *c * (old_lan->n_conns - j - 1)); + old_lan->n_conns--; + break; + } + } + } + + /* Connect to new_lan. */ + b->ports[port_no] = new_lan; + if (new_lan) { + int conn_no = new_lan->n_conns++; + assert(conn_no < ARRAY_SIZE(new_lan->conns)); + new_lan->conns[conn_no].bridge = b; + new_lan->conns[conn_no].port_no = port_no; + } +} + +static void +new_port(struct bridge *b, struct lan *lan, int path_cost) +{ + int port_no = b->n_ports++; + struct stp_port *p = stp_get_port(b->stp, port_no); + assert(port_no < ARRAY_SIZE(b->ports)); + b->ports[port_no] = NULL; + stp_port_set_path_cost(p, path_cost); + stp_port_enable(p); + reconnect_port(b, port_no, lan); +} + +static void +dump(struct test_case *tc) +{ + int i; + + for (i = 0; i < tc->n_bridges; i++) { + struct bridge *b = tc->bridges[i]; + struct stp *stp = b->stp; + int j; + + printf("%s:", stp_get_name(stp)); + if (stp_is_root_bridge(stp)) { + printf(" root"); + } + printf("\n"); + for (j = 0; j < b->n_ports; j++) { + struct stp_port *p = stp_get_port(stp, j); + enum stp_state state = stp_port_get_state(p); + + printf("\tport %d", j); + if (b->ports[j]) { + printf(" (lan %s)", b->ports[j]->name); + } else { + printf(" (disconnected)"); + } + printf(": %s", stp_state_name(state)); + if (p == stp_get_root_port(stp)) { + printf(" (root port, root_path_cost=%u)", stp_get_root_path_cost(stp)); + } + printf("\n"); + } + } +} + +static void dump_lan_tree(struct test_case *, struct lan *, int level); + +static void +dump_bridge_tree(struct test_case *tc, struct bridge *b, int level) +{ + int i; + + if (b->reached) { + return; + } + b->reached = true; + for (i = 0; i < level; i++) { + printf("\t"); + } + printf("%s\n", stp_get_name(b->stp)); + for (i = 0; i < b->n_ports; i++) { + struct lan *lan = b->ports[i]; + struct stp_port *p = stp_get_port(b->stp, i); + if (stp_port_get_state(p) == STP_FORWARDING && lan) { + dump_lan_tree(tc, lan, level + 1); + } + } +} + +static void +dump_lan_tree(struct test_case *tc, struct lan *lan, int level) +{ + int i; + + if (lan->reached) { + return; + } + lan->reached = true; + for (i = 0; i < level; i++) { + printf("\t"); + } + printf("%s\n", lan->name); + for (i = 0; i < lan->n_conns; i++) { + struct bridge *b = lan->conns[i].bridge; + dump_bridge_tree(tc, b, level + 1); + } +} + +static void +tree(struct test_case *tc) +{ + int i; + + for (i = 0; i < tc->n_bridges; i++) { + struct bridge *b = tc->bridges[i]; + b->reached = false; + } + for (i = 0; i < tc->n_lans; i++) { + struct lan *lan = tc->lans[i]; + lan->reached = false; + } + for (i = 0; i < tc->n_bridges; i++) { + struct bridge *b = tc->bridges[i]; + struct stp *stp = b->stp; + if (stp_is_root_bridge(stp)) { + dump_bridge_tree(tc, b, 0); + } + } +} + +static void +simulate(struct test_case *tc, int granularity) +{ + int time; + + for (time = 0; time < 256 * 180; time += granularity) { + int round_trips; + int i; + + for (i = 0; i < tc->n_bridges; i++) { + stp_tick(tc->bridges[i]->stp, granularity); + } + for (round_trips = 0; round_trips < granularity; round_trips++) { + bool any = false; + for (i = 0; i < tc->n_bridges; i++) { + struct bridge *b = tc->bridges[i]; + for (; b->rxq_tail != b->rxq_head; b->rxq_tail++) { + struct bpdu *bpdu = &b->rxq[b->rxq_tail % RXQ_SIZE]; + stp_received_bpdu(stp_get_port(b->stp, bpdu->port_no), + bpdu->data, bpdu->size); + any = true; + } + } + if (!any) { + break; + } + } + } +} + +static void +err(const char *message, ...) + PRINTF_FORMAT(1, 2) + NO_RETURN; + +static void +err(const char *message, ...) +{ + va_list args; + + fprintf(stderr, "%s:%d:%td: ", file_name, line_number, pos - line); + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + putc('\n', stderr); + + exit(EXIT_FAILURE); +} + +static void +warn(const char *message, ...) + PRINTF_FORMAT(1, 2); + +static void +warn(const char *message, ...) +{ + va_list args; + + fprintf(stderr, "%s:%d: ", file_name, line_number); + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + putc('\n', stderr); + + n_warnings++; +} + +static bool +get_token(void) +{ + char *start; + + while (isspace((unsigned char) *pos)) { + pos++; + } + if (*pos == '\0') { + token = NULL; + return false; + } + + start = pos; + if (isalpha((unsigned char) *pos)) { + while (isalpha((unsigned char) *++pos)) { + continue; + } + } else if (isdigit((unsigned char) *pos)) { + if (*pos == '0' && (pos[1] == 'x' || pos[1] == 'X')) { + pos += 2; + while (isxdigit((unsigned char) *pos)) { + pos++; + } + } else { + while (isdigit((unsigned char) *++pos)) { + continue; + } + } + } else { + pos++; + } + + free(token); + token = xmemdup0(start, pos - start); + return true; +} + +static bool +get_int(int *intp) +{ + char *save_pos = pos; + if (token && isdigit((unsigned char) *token)) { + *intp = strtol(token, NULL, 0); + get_token(); + return true; + } else { + pos = save_pos; + return false; + } +} + +static bool +match(const char *want) +{ + if (token && !strcmp(want, token)) { + get_token(); + return true; + } else { + return false; + } +} + +static int +must_get_int(void) +{ + int x; + if (!get_int(&x)) { + err("expected integer"); + } + return x; +} + +static void +must_match(const char *want) +{ + if (!match(want)) { + err("expected \"%s\"", want); + } +} + +int +main(int argc, char *argv[]) +{ + struct test_case *tc; + FILE *input_file; + int i; + + if (argc != 2) { - fatal(0, "usage: test-stp INPUT.STP\n"); ++ ofp_fatal(0, "usage: test-stp INPUT.STP\n"); + } + file_name = argv[1]; + + input_file = fopen(file_name, "r"); + if (!input_file) { - fatal(errno, "error opening \"%s\"", file_name); ++ ofp_fatal(errno, "error opening \"%s\"", file_name); + } + + tc = new_test_case(); + for (i = 0; i < 26; i++) { + char name[2]; + name[0] = 'a' + i; + name[1] = '\0'; + new_lan(tc, name); + } + + for (line_number = 1; fgets(line, sizeof line, input_file); + line_number++) + { + char *newline, *hash; + + newline = strchr(line, '\n'); + if (newline) { + *newline = '\0'; + } + hash = strchr(line, '#'); + if (hash) { + *hash = '\0'; + } + + pos = line; + if (!get_token()) { + continue; + } + if (match("bridge")) { + struct bridge *bridge; + int bridge_no, port_no; + + bridge_no = must_get_int(); + if (bridge_no < tc->n_bridges) { + bridge = tc->bridges[bridge_no]; + } else if (bridge_no == tc->n_bridges) { + bridge = new_bridge(tc, must_get_int()); + } else { + err("bridges must be numbered consecutively from 0"); + } + if (match("^")) { + stp_set_bridge_priority(bridge->stp, must_get_int()); + } + + if (match("=")) { + for (port_no = 0; port_no < STP_MAX_PORTS; port_no++) { + struct stp_port *p = stp_get_port(bridge->stp, port_no); + if (!token || match("X")) { + stp_port_disable(p); + } else if (match("_")) { + /* Nothing to do. */ + } else { + struct lan *lan; + int path_cost; + + if (!strcmp(token, "0")) { + lan = NULL; + } else if (strlen(token) == 1 && islower(*token)) { + lan = tc->lans[*token - 'a']; + } else { + err("%s is not a valid LAN name " + "(0 or a lowercase letter)", token); + } + get_token(); + + path_cost = match(":") ? must_get_int() : 10; + if (port_no < bridge->n_ports) { + stp_port_set_path_cost(p, path_cost); + stp_port_enable(p); + reconnect_port(bridge, port_no, lan); + } else if (port_no == bridge->n_ports) { + new_port(bridge, lan, path_cost); + } else { + err("ports must be numbered consecutively"); + } + if (match("^")) { + stp_port_set_priority(p, must_get_int()); + } + } + } + } + } else if (match("run")) { + simulate(tc, must_get_int()); + } else if (match("dump")) { + dump(tc); + } else if (match("tree")) { + tree(tc); + } else if (match("check")) { + struct bridge *b; + struct stp *stp; + int bridge_no, port_no; + + bridge_no = must_get_int(); + if (bridge_no >= tc->n_bridges) { + err("no bridge numbered %d", bridge_no); + } + b = tc->bridges[bridge_no]; + stp = b->stp; + + must_match("="); + + if (match("rootid")) { + uint64_t rootid; + must_match(":"); + rootid = must_get_int(); + if (match("^")) { + rootid |= (uint64_t) must_get_int() << 48; + } else { + rootid |= UINT64_C(0x8000) << 48; + } + if (stp_get_designated_root(stp) != rootid) { + warn("%s: root %"PRIx64", not %"PRIx64, + stp_get_name(stp), stp_get_designated_root(stp), + rootid); + } + } + + if (match("root")) { + if (stp_get_root_path_cost(stp)) { + warn("%s: root path cost of root is %u but should be 0", + stp_get_name(stp), stp_get_root_path_cost(stp)); + } + if (!stp_is_root_bridge(stp)) { + warn("%s: root is %"PRIx64", not %"PRIx64, + stp_get_name(stp), + stp_get_designated_root(stp), stp_get_bridge_id(stp)); + } + for (port_no = 0; port_no < b->n_ports; port_no++) { + struct stp_port *p = stp_get_port(stp, port_no); + enum stp_state state = stp_port_get_state(p); + if (!(state & (STP_DISABLED | STP_FORWARDING))) { + warn("%s: root port %d in state %s", + stp_get_name(b->stp), port_no, + stp_state_name(state)); + } + } + } else { + for (port_no = 0; port_no < STP_MAX_PORTS; port_no++) { + struct stp_port *p = stp_get_port(stp, port_no); + enum stp_state state; + if (token == NULL || match("D")) { + state = STP_DISABLED; + } else if (match("B")) { + state = STP_BLOCKING; + } else if (match("Li")) { + state = STP_LISTENING; + } else if (match("Le")) { + state = STP_LEARNING; + } else if (match("F")) { + state = STP_FORWARDING; + } else if (match("_")) { + continue; + } else { + err("unknown port state %s", token); + } + if (stp_port_get_state(p) != state) { + warn("%s port %d: state is %s but should be %s", + stp_get_name(stp), port_no, + stp_state_name(stp_port_get_state(p)), + stp_state_name(state)); + } + if (state == STP_FORWARDING) { + struct stp_port *root_port = stp_get_root_port(stp); + if (match(":")) { + int root_path_cost = must_get_int(); + if (p != root_port) { + warn("%s: port %d is not the root port", + stp_get_name(stp), port_no); + if (!root_port) { + warn("%s: (there is no root port)", + stp_get_name(stp)); + } else { + warn("%s: (port %d is the root port)", + stp_get_name(stp), + stp_port_no(root_port)); + } + } else if (root_path_cost + != stp_get_root_path_cost(stp)) { + warn("%s: root path cost is %u, should be %d", + stp_get_name(stp), + stp_get_root_path_cost(stp), + root_path_cost); + } + } else if (p == root_port) { + warn("%s: port %d is the root port but " + "not expected to be", + stp_get_name(stp), port_no); + } + } + } + } + if (n_warnings) { + exit(EXIT_FAILURE); + } + } + if (get_token()) { + err("trailing garbage on line"); + } + } + + return 0; +} diff --cc utilities/dpctl.c index af0593f5,302d4d1b..1d9897d7 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@@ -53,9 -53,8 +53,9 @@@ #include "command-line.h" #include "compiler.h" #include "dpif.h" +#include "nicira-ext.h" #include "ofp-print.h" + #include "ofpbuf.h" #include "openflow.h" #include "packets.h" #include "random.h" @@@ -328,14 -339,8 +328,14 @@@ static void do_del_port(int argc UNUSED /* Generic commands. */ +static void +open_vconn(const char *name, struct vconn **vconnp) +{ + run(vconn_open_block(name, OFP_VERSION, vconnp), "connecting to %s", name); +} + static void * - alloc_stats_request(size_t body_len, uint16_t type, struct buffer **bufferp) + alloc_stats_request(size_t body_len, uint16_t type, struct ofpbuf **bufferp) { struct ofp_stats_request *rq; rq = make_openflow((offsetof(struct ofp_stats_request, body) @@@ -353,13 -358,13 +353,13 @@@ send_openflow_buffer(struct vconn *vcon } static void - dump_transaction(const char *vconn_name, struct buffer *request) + dump_transaction(const char *vconn_name, struct ofpbuf *request) { struct vconn *vconn; - struct buffer *reply; + struct ofpbuf *reply; update_openflow_length(request); - run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name); + open_vconn(vconn_name, &vconn); run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name); ofp_print(stdout, reply->data, reply->size, 1); vconn_close(vconn); @@@ -422,32 -427,12 +422,32 @@@ do_show(int argc UNUSED, char *argv[] static void do_status(int argc, char *argv[]) { - struct ofpbuf *request; - alloc_stats_request(0, OFPST_SWITCH, &request); + struct nicira_header *request, *reply; + struct vconn *vconn; - struct buffer *b; ++ struct ofpbuf *b; + + request = make_openflow(sizeof *request, OFPT_VENDOR, &b); + request->vendor_id = htonl(NX_VENDOR_ID); + request->subtype = htonl(NXT_STATUS_REQUEST); if (argc > 2) { - buffer_put(b, argv[2], strlen(argv[2])); - ofpbuf_put(request, argv[2], strlen(argv[2])); ++ ofpbuf_put(b, argv[2], strlen(argv[2])); } - dump_stats_transaction(argv[1], request); + open_vconn(argv[1], &vconn); + run(vconn_transact(vconn, b, &b), "talking to %s", argv[1]); + vconn_close(vconn); + + if (b->size < sizeof *reply) { - fatal(0, "short reply (%zu bytes)", b->size); ++ ofp_fatal(0, "short reply (%zu bytes)", b->size); + } + reply = b->data; + if (reply->header.type != OFPT_VENDOR + || reply->vendor_id != ntohl(NX_VENDOR_ID) + || reply->subtype != ntohl(NXT_STATUS_REPLY)) { + ofp_print(stderr, b->data, b->size, 2); - fatal(0, "bad reply"); ++ ofp_fatal(0, "bad reply"); + } + + fwrite(reply + 1, b->size, 1, stdout); } static void @@@ -829,12 -816,12 +831,12 @@@ static void do_add_flows(int argc, cha file = fopen(argv[2], "r"); if (file == NULL) { - fatal(errno, "%s: open", argv[2]); + ofp_fatal(errno, "%s: open", argv[2]); } - run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]); + open_vconn(argv[1], &vconn); while (fgets(line, sizeof line, file)) { - struct buffer *buffer; + struct ofpbuf *buffer; struct ofp_flow_mod *ofm; uint16_t priority, idle_timeout, hard_timeout; size_t size; @@@ -879,8 -866,8 +881,8 @@@ static void do_del_flows(int argc, cha struct vconn *vconn; uint16_t priority; - run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]); + open_vconn(argv[1], &vconn); - struct buffer *buffer; + struct ofpbuf *buffer; struct ofp_flow_mod *ofm; size_t size; @@@ -902,29 -889,6 +904,29 @@@ vconn_close(vconn); } +static void +do_monitor(int argc UNUSED, char *argv[]) +{ + struct vconn *vconn; + const char *name; + + /* If the user specified, e.g., "nl:0", append ":1" to it to ensure that + * the connection will subscribe to listen for asynchronous messages, such + * as packet-in messages. */ + if (!strncmp(argv[1], "nl:", 3) && strrchr(argv[1], ':') == &argv[1][2]) { + name = xasprintf("%s:1", argv[1]); + } else { + name = argv[1]; + } + open_vconn(argv[1], &vconn); + for (;;) { - struct buffer *b; ++ struct ofpbuf *b; + run(vconn_recv_block(vconn, &b), "vconn_recv"); + ofp_print(stderr, b->data, b->size, 2); - buffer_delete(b); ++ ofpbuf_delete(b); + } +} + static void do_dump_ports(int argc, char *argv[]) { @@@ -934,17 -898,17 +936,17 @@@ static void do_probe(int argc, char *argv[]) { - struct buffer *request; + struct ofpbuf *request; struct vconn *vconn; - struct buffer *reply; + struct ofpbuf *reply; make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request); - run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]); + open_vconn(argv[1], &vconn); run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]); if (reply->size != request->size) { - fatal(0, "reply does not match request"); + ofp_fatal(0, "reply does not match request"); } - buffer_delete(reply); + ofpbuf_delete(reply); vconn_close(vconn); } @@@ -1036,13 -1000,13 +1038,13 @@@ do_ping(int argc, char *argv[] payload = argc > 2 ? atoi(argv[2]) : 64; if (payload > max_payload) { - fatal(0, "payload must be between 0 and %zu bytes", max_payload); + ofp_fatal(0, "payload must be between 0 and %zu bytes", max_payload); } - run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]); + open_vconn(argv[1], &vconn); for (i = 0; i < 10; i++) { struct timeval start, end; - struct buffer *request, *reply; + struct ofpbuf *request, *reply; struct ofp_header *rq_hdr, *rpy_hdr; rq_hdr = make_openflow(sizeof(struct ofp_header) + payload, @@@ -1095,10 -1059,10 +1097,10 @@@ do_benchmark(int argc, char *argv[] printf("Sending %d packets * %u bytes (with header) = %u bytes total\n", count, message_size, count * message_size); - run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]); + open_vconn(argv[1], &vconn); gettimeofday(&start, NULL); for (i = 0; i < count; i++) { - struct buffer *request, *reply; + struct ofpbuf *request, *reply; struct ofp_header *rq_hdr; rq_hdr = make_openflow(message_size, OFPT_ECHO_REQUEST, &request);