X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fvconn.c;h=d8807fda3e521b46d9a975138f3c408ba6724661;hb=a0bc29a541fc7dc6e20137d5558e2094d614e6ab;hp=b5e110204eef1d64d925bb79731237601038f759;hpb=2b35e1475ef0c274495aa04d60b054288971d503;p=openvswitch diff --git a/lib/vconn.c b/lib/vconn.c index b5e11020..d8807fda 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -90,7 +90,8 @@ check_vconn_classes(void) struct vconn_class *class = vconn_classes[i]; assert(class->name != NULL); assert(class->open != NULL); - if (class->close || class->recv || class->send || class->wait) { + if (class->close || class->recv || class->send + || class->run || class->run_wait || class->wait) { assert(class->close != NULL); assert(class->recv != NULL); assert(class->send != NULL); @@ -119,7 +120,7 @@ check_vconn_classes(void) * connection methods supported by the vconn. If 'bootstrap' is true, also * advertises options to bootstrap the CA certificate. */ void -vconn_usage(bool active, bool passive, bool bootstrap UNUSED) +vconn_usage(bool active, bool passive, bool bootstrap OVS_UNUSED) { /* Really this should be implemented via callbacks into the vconn * providers, but that seems too heavy-weight to bother with at the @@ -139,12 +140,12 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED) if (passive) { printf("Passive OpenFlow connection methods:\n"); - printf(" ptcp:[PORT] " - "listen to TCP PORT (default: %d)\n", + printf(" ptcp:[PORT][:IP] " + "listen to TCP PORT (default: %d) on IP\n", OFP_TCP_PORT); #ifdef HAVE_OPENSSL - printf(" pssl:[PORT] " - "listen for SSL on PORT (default: %d)\n", + printf(" pssl:[PORT][:IP] " + "listen for SSL on PORT (default: %d) on IP\n", OFP_SSL_PORT); #endif printf(" punix:FILE " @@ -163,6 +164,42 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED) #endif } +/* Given 'name', a connection name in the form "TYPE:ARGS", stores the class + * named "TYPE" into '*classp' and returns 0. Returns EAFNOSUPPORT and stores + * a null pointer into '*classp' if 'name' is in the wrong form or if no such + * class exists. */ +static int +vconn_lookup_class(const char *name, struct vconn_class **classp) +{ + size_t prefix_len; + + prefix_len = strcspn(name, ":"); + if (name[prefix_len] != '\0') { + size_t i; + + for (i = 0; i < ARRAY_SIZE(vconn_classes); i++) { + struct vconn_class *class = vconn_classes[i]; + if (strlen(class->name) == prefix_len + && !memcmp(class->name, name, prefix_len)) { + *classp = class; + return 0; + } + } + } + + *classp = NULL; + return EAFNOSUPPORT; +} + +/* Returns 0 if 'name' is a connection name in the form "TYPE:ARGS" and TYPE is + * a supported connection type, otherwise EAFNOSUPPORT. */ +int +vconn_verify_name(const char *name) +{ + struct vconn_class *class; + return vconn_lookup_class(name, &class); +} + /* Attempts to connect to an OpenFlow device. 'name' is a connection name in * the form "TYPE:ARGS", where TYPE is an active vconn class's name and ARGS * are vconn class-specific. @@ -177,35 +214,57 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED) int vconn_open(const char *name, int min_version, struct vconn **vconnp) { - size_t prefix_len; - size_t i; + struct vconn_class *class; + struct vconn *vconn; + char *suffix_copy; + int error; COVERAGE_INC(vconn_open); check_vconn_classes(); + /* Look up the class. */ + error = vconn_lookup_class(name, &class); + if (!class) { + goto error; + } + + /* Call class's "open" function. */ + suffix_copy = xstrdup(strchr(name, ':') + 1); + error = class->open(name, suffix_copy, &vconn); + free(suffix_copy); + if (error) { + goto error; + } + + /* Success. */ + assert(vconn->state != VCS_CONNECTING || vconn->class->connect); + vconn->min_version = min_version; + *vconnp = vconn; + return 0; + +error: *vconnp = NULL; - prefix_len = strcspn(name, ":"); - if (prefix_len == strlen(name)) { - return EAFNOSUPPORT; + return error; +} + +/* Allows 'vconn' to perform maintenance activities, such as flushing output + * buffers. */ +void +vconn_run(struct vconn *vconn) +{ + if (vconn->class->run) { + (vconn->class->run)(vconn); } - for (i = 0; i < ARRAY_SIZE(vconn_classes); i++) { - struct vconn_class *class = vconn_classes[i]; - if (strlen(class->name) == prefix_len - && !memcmp(class->name, name, prefix_len)) { - struct vconn *vconn; - char *suffix_copy = xstrdup(name + prefix_len + 1); - int retval = class->open(name, suffix_copy, &vconn); - free(suffix_copy); - if (!retval) { - assert(vconn->state != VCS_CONNECTING - || vconn->class->connect); - vconn->min_version = min_version; - *vconnp = vconn; - } - return retval; - } +} + +/* Arranges for the poll loop to wake up when 'vconn' needs to perform + * maintenance activities. */ +void +vconn_run_wait(struct vconn *vconn) +{ + if (vconn->class->run_wait) { + (vconn->class->run_wait)(vconn); } - return EAFNOSUPPORT; } int @@ -216,6 +275,8 @@ vconn_open_block(const char *name, int min_version, struct vconn **vconnp) error = vconn_open(name, min_version, &vconn); while (error == EAGAIN) { + vconn_run(vconn); + vconn_run_wait(vconn); vconn_connect_wait(vconn); poll_block(); error = vconn_connect(vconn); @@ -364,7 +425,7 @@ vcs_recv_hello(struct vconn *vconn) if (retval != EAGAIN) { vconn->state = VCS_DISCONNECTED; - vconn->error = retval; + vconn->error = retval == EOF ? ECONNRESET : retval; } } @@ -458,10 +519,7 @@ vconn_recv(struct vconn *vconn, struct ofpbuf **msgp) static int do_recv(struct vconn *vconn, struct ofpbuf **msgp) { - int retval; - -again: - retval = (vconn->class->recv)(vconn, msgp); + int retval = (vconn->class->recv)(vconn, msgp); if (!retval) { struct ofp_header *oh; @@ -481,20 +539,6 @@ again: && oh->type != OFPT_VENDOR) { if (vconn->version < 0) { - if (oh->type == OFPT_PACKET_IN - || oh->type == OFPT_FLOW_EXPIRED - || oh->type == OFPT_PORT_STATUS) { - /* The kernel datapath is stateless and doesn't really - * support version negotiation, so it can end up sending - * these asynchronous message before version negotiation - * is complete. Just ignore them. - * - * (After we move OFPT_PORT_STATUS messages from the kernel - * into secchan, we won't get those here, since secchan - * does proper version negotiation.) */ - ofpbuf_delete(*msgp); - goto again; - } VLOG_ERR_RL(&bad_ofmsg_rl, "%s: received OpenFlow message type %"PRIu8" " "before version negotiation complete", @@ -564,6 +608,8 @@ vconn_send_block(struct vconn *vconn, struct ofpbuf *msg) { int retval; while ((retval = vconn_send(vconn, msg)) == EAGAIN) { + vconn_run(vconn); + vconn_run_wait(vconn); vconn_send_wait(vconn); poll_block(); } @@ -576,6 +622,8 @@ vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp) { int retval; while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) { + vconn_run(vconn); + vconn_run_wait(vconn); vconn_recv_wait(vconn); poll_block(); } @@ -681,6 +729,42 @@ vconn_send_wait(struct vconn *vconn) vconn_wait(vconn, WAIT_SEND); } +/* Given 'name', a connection name in the form "TYPE:ARGS", stores the class + * named "TYPE" into '*classp' and returns 0. Returns EAFNOSUPPORT and stores + * a null pointer into '*classp' if 'name' is in the wrong form or if no such + * class exists. */ +static int +pvconn_lookup_class(const char *name, struct pvconn_class **classp) +{ + size_t prefix_len; + + prefix_len = strcspn(name, ":"); + if (name[prefix_len] != '\0') { + size_t i; + + for (i = 0; i < ARRAY_SIZE(pvconn_classes); i++) { + struct pvconn_class *class = pvconn_classes[i]; + if (strlen(class->name) == prefix_len + && !memcmp(class->name, name, prefix_len)) { + *classp = class; + return 0; + } + } + } + + *classp = NULL; + return EAFNOSUPPORT; +} + +/* Returns 0 if 'name' is a connection name in the form "TYPE:ARGS" and TYPE is + * a supported connection type, otherwise EAFNOSUPPORT. */ +int +pvconn_verify_name(const char *name) +{ + struct pvconn_class *class; + return pvconn_lookup_class(name, &class); +} + /* Attempts to start listening for OpenFlow connections. 'name' is a * connection name in the form "TYPE:ARGS", where TYPE is an passive vconn * class's name and ARGS are vconn class-specific. @@ -691,30 +775,34 @@ vconn_send_wait(struct vconn *vconn) int pvconn_open(const char *name, struct pvconn **pvconnp) { - size_t prefix_len; - size_t i; + struct pvconn_class *class; + struct pvconn *pvconn; + char *suffix_copy; + int error; check_vconn_classes(); - *pvconnp = NULL; - prefix_len = strcspn(name, ":"); - if (prefix_len == strlen(name)) { - return EAFNOSUPPORT; + /* Look up the class. */ + error = pvconn_lookup_class(name, &class); + if (!class) { + goto error; } - for (i = 0; i < ARRAY_SIZE(pvconn_classes); i++) { - struct pvconn_class *class = pvconn_classes[i]; - if (strlen(class->name) == prefix_len - && !memcmp(class->name, name, prefix_len)) { - char *suffix_copy = xstrdup(name + prefix_len + 1); - int retval = class->listen(name, suffix_copy, pvconnp); - free(suffix_copy); - if (retval) { - *pvconnp = NULL; - } - return retval; - } + + /* Call class's "open" function. */ + suffix_copy = xstrdup(strchr(name, ':') + 1); + error = class->listen(name, suffix_copy, &pvconn); + free(suffix_copy); + if (error) { + goto error; } - return EAFNOSUPPORT; + + /* Success. */ + *pvconnp = pvconn; + return 0; + +error: + *pvconnp = NULL; + return error; } /* Returns the name that was used to open 'pvconn'. The caller must not @@ -876,16 +964,19 @@ make_flow_mod(uint16_t command, const flow_t *flow, size_t actions_len) ofm->header.version = OFP_VERSION; ofm->header.type = OFPT_FLOW_MOD; ofm->header.length = htons(size); + ofm->cookie = 0; ofm->match.wildcards = htonl(0); ofm->match.in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL : flow->in_port); memcpy(ofm->match.dl_src, flow->dl_src, sizeof ofm->match.dl_src); memcpy(ofm->match.dl_dst, flow->dl_dst, sizeof ofm->match.dl_dst); ofm->match.dl_vlan = flow->dl_vlan; + ofm->match.dl_vlan_pcp = flow->dl_vlan_pcp; ofm->match.dl_type = flow->dl_type; ofm->match.nw_src = flow->nw_src; ofm->match.nw_dst = flow->nw_dst; ofm->match.nw_proto = flow->nw_proto; + ofm->match.nw_tos = flow->nw_tos; ofm->match.tp_src = flow->tp_src; ofm->match.tp_dst = flow->tp_dst; ofm->command = htons(command); @@ -928,6 +1019,28 @@ make_add_simple_flow(const flow_t *flow, return buffer; } +struct ofpbuf * +make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason, + const struct ofpbuf *payload, int max_send_len) +{ + struct ofp_packet_in *opi; + struct ofpbuf *buf; + int send_len; + + send_len = MIN(max_send_len, payload->size); + buf = ofpbuf_new(sizeof *opi + send_len); + opi = put_openflow_xid(offsetof(struct ofp_packet_in, data), + OFPT_PACKET_IN, 0, buf); + opi->buffer_id = htonl(buffer_id); + opi->total_len = htons(payload->size); + opi->in_port = htons(in_port); + opi->reason = reason; + ofpbuf_put(buf, payload->data, send_len); + update_openflow_length(buf); + + return buf; +} + struct ofpbuf * make_packet_out(const struct ofpbuf *packet, uint32_t buffer_id, uint16_t in_port, @@ -1037,10 +1150,10 @@ check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size) if (got_size != size) { char *type_name = ofp_message_type_to_string(type); VLOG_WARN_RL(&bad_ofmsg_rl, - "received %s message of length %"PRIu16" (expected %zu)", + "received %s message of length %zu (expected %zu)", type_name, got_size, size); free(type_name); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); } return 0; @@ -1072,22 +1185,22 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type, got_size = ntohs(msg->length); if (got_size < min_size) { char *type_name = ofp_message_type_to_string(type); - VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %"PRIu16" " + VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %zu " "(expected at least %zu)", type_name, got_size, min_size); free(type_name); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); } if ((got_size - min_size) % array_elt_size) { char *type_name = ofp_message_type_to_string(type); VLOG_WARN_RL(&bad_ofmsg_rl, - "received %s message of bad length %"PRIu16": the " + "received %s message of bad length %zu: the " "excess over %zu (%zu) is not evenly divisible by %zu " "(remainder is %zu)", type_name, got_size, min_size, got_size - min_size, array_elt_size, (got_size - min_size) % array_elt_size); free(type_name); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); } if (n_array_elts) { *n_array_elts = (got_size - min_size) / array_elt_size; @@ -1114,16 +1227,16 @@ check_ofp_packet_out(const struct ofp_header *oh, struct ofpbuf *data, actions_len = ntohs(opo->actions_len); if (actions_len > extra) { - VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %zu bytes of actions " + VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions " "but message has room for only %zu bytes", actions_len, extra); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); } if (actions_len % sizeof(union ofp_action)) { - VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %zu bytes of actions, " + VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions, " "which is not a multiple of %zu", actions_len, sizeof(union ofp_action)); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); } n_actions = actions_len / sizeof(union ofp_action); @@ -1247,19 +1360,17 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports) { int error; - switch (a->type) { + switch (ntohs(a->type)) { case OFPAT_OUTPUT: error = check_action_port(ntohs(a->output.port), max_ports); - if (error) { - return error; - } - return check_action_exact_len(a, len, 8); + return error ? error : check_action_exact_len(a, len, 8); case OFPAT_SET_VLAN_VID: case OFPAT_SET_VLAN_PCP: case OFPAT_STRIP_VLAN: case OFPAT_SET_NW_SRC: case OFPAT_SET_NW_DST: + case OFPAT_SET_NW_TOS: case OFPAT_SET_TP_SRC: case OFPAT_SET_TP_DST: return check_action_exact_len(a, len, 8); @@ -1269,28 +1380,15 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports) return check_action_exact_len(a, len, 16); case OFPAT_VENDOR: - if (a->vendor.vendor == htonl(NX_VENDOR_ID)) { - return check_nicira_action(a, len); - } else { - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR); - } - break; + return (a->vendor.vendor == htonl(NX_VENDOR_ID) + ? check_nicira_action(a, len) + : ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR)); default: - VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16, a->type); + VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16, + ntohs(a->type)); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE); } - - if (!len) { - VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0"); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } - if (len % ACTION_ALIGNMENT) { - VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple of %d", - len, ACTION_ALIGNMENT); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } - return 0; } int @@ -1307,10 +1405,18 @@ validate_actions(const union ofp_action *actions, size_t n_actions, if (n_slots > slots_left) { VLOG_DBG_RL(&bad_ofmsg_rl, - "action requires %u slots but only %td remain", + "action requires %u slots but only %u remain", n_slots, slots_left); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); + } else if (!len) { + VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0"); + return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); + } else if (len % ACTION_ALIGNMENT) { + VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple " + "of %d", len, ACTION_ALIGNMENT); + return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); } + error = check_action(a, len, max_ports); if (error) { return error; @@ -1355,7 +1461,7 @@ normalize_match(struct ofp_match *m) if (wc & OFPFW_DL_TYPE) { m->dl_type = 0; - /* Can't sensibly m on network or transport headers if the + /* Can't sensibly match on network or transport headers if the * data link type is unknown. */ wc |= OFPFW_NW | OFPFW_TP; m->nw_src = m->nw_dst = m->nw_proto = 0; @@ -1364,7 +1470,7 @@ normalize_match(struct ofp_match *m) if (wc & OFPFW_NW_PROTO) { m->nw_proto = 0; - /* Can't sensibly m on transport headers if the network + /* Can't sensibly match on transport headers if the network * protocol is unknown. */ wc |= OFPFW_TP; m->tp_src = m->tp_dst = 0; @@ -1379,7 +1485,7 @@ normalize_match(struct ofp_match *m) } } else { /* Transport layer fields will always be extracted as zeros, so we - * can do an exact-m on those values. */ + * can do an exact-match on those values. */ wc &= ~OFPFW_TP; m->tp_src = m->tp_dst = 0; } @@ -1389,9 +1495,20 @@ normalize_match(struct ofp_match *m) if (wc & OFPFW_NW_DST_MASK) { m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); } + } else if (m->dl_type == htons(ETH_TYPE_ARP)) { + if (wc & OFPFW_NW_PROTO) { + m->nw_proto = 0; + } + if (wc & OFPFW_NW_SRC_MASK) { + m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT); + } + if (wc & OFPFW_NW_DST_MASK) { + m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); + } + m->tp_src = m->tp_dst = 0; } else { /* Network and transport layer fields will always be extracted as - * zeros, so we can do an exact-m on those values. */ + * zeros, so we can do an exact-match on those values. */ wc &= ~(OFPFW_NW | OFPFW_TP); m->nw_proto = m->nw_src = m->nw_dst = 0; m->tp_src = m->tp_dst = 0; @@ -1405,9 +1522,26 @@ normalize_match(struct ofp_match *m) m->wildcards = htonl(wc); } +/* Initializes 'vconn' as a new vconn named 'name', implemented via 'class'. + * The initial connection status, supplied as 'connect_status', is interpreted + * as follows: + * + * - 0: 'vconn' is connected. Its 'send' and 'recv' functions may be + * called in the normal fashion. + * + * - EAGAIN: 'vconn' is trying to complete a connection. Its 'connect' + * function should be called to complete the connection. + * + * - Other positive errno values indicate that the connection failed with + * the specified error. + * + * After calling this function, vconn_close() must be used to destroy 'vconn', + * otherwise resources will be leaked. + * + * The caller retains ownership of 'name'. */ void vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status, - const char *name, bool reconnectable) + const char *name) { vconn->class = class; vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING @@ -1421,7 +1555,7 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status, vconn->local_ip = 0; vconn->local_port = 0; vconn->name = xstrdup(name); - vconn->reconnectable = reconnectable; + assert(vconn->state != VCS_CONNECTING || class->connect); } void