/* Helper function for nl_dump_next(). */ static int nl_dump_recv(struct nl_dump *dump) { struct nlmsghdr *nlmsghdr; int retval; retval = nl_sock_recv__(dump->sock, &dump->buffer, true); if (retval) { return retval == EINTR ? EAGAIN : retval; } nlmsghdr = nl_msg_nlmsghdr(&dump->buffer); if (dump->seq != nlmsghdr->nlmsg_seq) { VLOG_DBG_RL(&rl, "ignoring seq %#"PRIx32" != expected %#"PRIx32, nlmsghdr->nlmsg_seq, dump->seq); return EAGAIN; } if (nl_msg_nlmsgerr(&dump->buffer, &retval)) { VLOG_INFO_RL(&rl, "netlink dump request error (%s)", ovs_strerror(retval)); return retval && retval != EAGAIN ? retval : EPROTO; } return 0; }
static void lswitch_handshake(struct lswitch *sw) { enum ofputil_protocol protocol; enum ofp_version version; send_features_request(sw); send_controller_id(sw); version = rconn_get_version(sw->rconn); protocol = ofputil_protocol_from_ofp_version(version); if (sw->default_flows) { struct ofpbuf *msg = NULL; int error = 0; size_t i; if (!(protocol & sw->usable_protocols)) { enum ofputil_protocol want = rightmost_1bit(sw->usable_protocols); while (!error) { msg = ofputil_encode_set_protocol(protocol, want, &protocol); if (!msg) { break; } error = rconn_send(sw->rconn, msg, NULL); } } if (protocol & sw->usable_protocols) { for (i = 0; !error && i < sw->n_default_flows; i++) { msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol); error = rconn_send(sw->rconn, msg, NULL); } if (error) { VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)", rconn_get_name(sw->rconn), ovs_strerror(error)); } } else { VLOG_INFO_RL(&rl, "%s: failed to set usable protocol", rconn_get_name(sw->rconn)); } } sw->protocol = protocol; }
/* Extracts the mac, IPv4 and IPv6 addresses from the * "nbrec_logical_router_port" parameter 'lrp'. Stores the IPv4 and * IPv6 addresses in the 'ipv4_addrs' and 'ipv6_addrs' fields of * 'laddrs', respectively. In addition, a link local IPv6 address * based on the 'mac' member of 'lrp' is added to the 'ipv6_addrs' * field. * * Return true if a valid 'mac' address is found in 'lrp', false otherwise. * * The caller must call destroy_lport_addresses(). */ bool extract_lrp_networks(const struct nbrec_logical_router_port *lrp, struct lport_addresses *laddrs) { memset(laddrs, 0, sizeof *laddrs); if (!eth_addr_from_string(lrp->mac, &laddrs->ea)) { laddrs->ea = eth_addr_zero; return false; } snprintf(laddrs->ea_s, sizeof laddrs->ea_s, ETH_ADDR_FMT, ETH_ADDR_ARGS(laddrs->ea)); for (int i = 0; i < lrp->n_networks; i++) { ovs_be32 ip4; struct in6_addr ip6; unsigned int plen; char *error; error = ip_parse_cidr(lrp->networks[i], &ip4, &plen); if (!error) { if (!ip4 || plen == 32) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "bad 'networks' %s", lrp->networks[i]); continue; } add_ipv4_netaddr(laddrs, ip4, plen); continue; } free(error); error = ipv6_parse_cidr(lrp->networks[i], &ip6, &plen); if (!error) { if (plen == 128) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "bad 'networks' %s", lrp->networks[i]); continue; } add_ipv6_netaddr(laddrs, ip6, plen); } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_INFO_RL(&rl, "invalid syntax '%s' in networks", lrp->networks[i]); free(error); } } /* Always add the IPv6 link local address. */ struct in6_addr lla; in6_generate_lla(laddrs->ea, &lla); add_ipv6_netaddr(laddrs, lla, 64); return true; }
/* Extracts the mac, IPv4 and IPv6 addresses from * 'address' which * should be of the format 'MAC [IP1 IP2 ..]" where IPn should be a * valid IPv4 or IPv6 address and stores them in the 'ipv4_addrs' and * 'ipv6_addrs' fields of 'laddrs'. * * Return true if at least 'MAC' is found in 'address', false otherwise. * * The caller must call destroy_lport_addresses(). */ bool extract_lsp_addresses(const char *address, struct lport_addresses *laddrs) { int ofs; bool success = extract_addresses(address, laddrs, &ofs); if (success && ofs < strlen(address)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", address); } return success; }
/* Extracts the mac, IPv4 and IPv6 addresses from * 'address' which * should be of the format 'MAC [IP1 IP2 ..]" where IPn should be a * valid IPv4 or IPv6 address and stores them in the 'ipv4_addrs' and * 'ipv6_addrs' fields of 'laddrs'. * * Return true if at least 'MAC' is found in 'address', false otherwise. * * The caller must call destroy_lport_addresses(). */ bool extract_lsp_addresses(const char *address, struct lport_addresses *laddrs) { memset(laddrs, 0, sizeof *laddrs); const char *buf = address; int buf_index = 0; const char *buf_end = buf + strlen(address); if (!ovs_scan_len(buf, &buf_index, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(laddrs->ea))) { laddrs->ea = eth_addr_zero; return false; } snprintf(laddrs->ea_s, sizeof laddrs->ea_s, ETH_ADDR_FMT, ETH_ADDR_ARGS(laddrs->ea)); ovs_be32 ip4; struct in6_addr ip6; unsigned int plen; char *error; /* Loop through the buffer and extract the IPv4/IPv6 addresses * and store in the 'laddrs'. Break the loop if invalid data is found. */ buf += buf_index; while (buf < buf_end) { buf_index = 0; error = ip_parse_cidr_len(buf, &buf_index, &ip4, &plen); if (!error) { add_ipv4_netaddr(laddrs, ip4, plen); buf += buf_index; continue; } free(error); error = ipv6_parse_cidr_len(buf, &buf_index, &ip6, &plen); if (!error) { add_ipv6_netaddr(laddrs, ip6, plen); } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", address); free(error); break; } buf += buf_index; } return true; }
static void queue_tx(struct lswitch *sw, struct ofpbuf *b) { int retval = rconn_send_with_limit(sw->rconn, b, sw->queued, 10); if (retval && retval != ENOTCONN) { if (retval == EAGAIN) { VLOG_INFO_RL(&rl, "%016llx: %s: tx queue overflow", sw->datapath_id, rconn_get_name(sw->rconn)); } else { VLOG_WARN_RL(&rl, "%016llx: %s: send: %s", sw->datapath_id, rconn_get_name(sw->rconn), ovs_strerror(retval)); } } }
/* Schedules 'msg' to be sent on 'rpc' and returns 'rpc''s status (as with * jsonrpc_get_status()). * * If 'msg' cannot be sent immediately, it is appended to a buffer. The caller * is responsible for ensuring that the amount of buffered data is somehow * limited. (jsonrpc_get_backlog() returns the amount of data currently * buffered in 'rpc'.) * * Always takes ownership of 'msg', regardless of success. */ int jsonrpc_send(struct jsonrpc *rpc, struct jsonrpc_msg *msg) { struct ofpbuf *buf; struct json *json; size_t length; char *s; if (rpc->status) { jsonrpc_msg_destroy(msg); return rpc->status; } jsonrpc_log_msg(rpc, "send", msg); json = jsonrpc_msg_to_json(msg); s = json_to_string(json, 0); length = strlen(s); json_destroy(json); buf = xmalloc(sizeof *buf); ofpbuf_use(buf, s, length); buf->size = length; list_push_back(&rpc->output, &buf->list_node); rpc->output_count++; rpc->backlog += length; if (rpc->output_count >= 50) { VLOG_INFO_RL(&rl, "excessive sending backlog, jsonrpc: %s, num of" " msgs: %"PRIuSIZE", backlog: %"PRIuSIZE".", rpc->name, rpc->output_count, rpc->backlog); } if (rpc->backlog == length) { jsonrpc_run(rpc); } return rpc->status; }
static int do_ca_cert_bootstrap(struct stream *stream) { struct ssl_stream *sslv = ssl_stream_cast(stream); STACK_OF(X509) *chain; X509 *cert; FILE *file; int error; int fd; chain = SSL_get_peer_cert_chain(sslv->ssl); if (!chain || !sk_X509_num(chain)) { VLOG_ERR("could not bootstrap CA cert: no certificate presented by " "peer"); return EPROTO; } cert = sk_X509_value(chain, sk_X509_num(chain) - 1); /* Check that 'cert' is self-signed. Otherwise it is not a CA * certificate and we should not attempt to use it as one. */ error = X509_check_issued(cert, cert); if (error) { VLOG_ERR("could not bootstrap CA cert: obtained certificate is " "not self-signed (%s)", X509_verify_cert_error_string(error)); if (sk_X509_num(chain) < 2) { VLOG_ERR("only one certificate was received, so probably the peer " "is not configured to send its CA certificate"); } return EPROTO; } fd = open(ca_cert.file_name, O_CREAT | O_EXCL | O_WRONLY, 0444); if (fd < 0) { if (errno == EEXIST) { VLOG_INFO_RL(&rl, "reading CA cert %s created by another process", ca_cert.file_name); stream_ssl_set_ca_cert_file__(ca_cert.file_name, true, true); return EPROTO; } else { VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s", ca_cert.file_name, ovs_strerror(errno)); return errno; } } file = fdopen(fd, "w"); if (!file) { error = errno; VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s", ovs_strerror(error)); unlink(ca_cert.file_name); return error; } if (!PEM_write_X509(file, cert)) { VLOG_ERR("could not bootstrap CA cert: PEM_write_X509 to %s failed: " "%s", ca_cert.file_name, ERR_error_string(ERR_get_error(), NULL)); fclose(file); unlink(ca_cert.file_name); return EIO; } if (fclose(file)) { error = errno; VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s", ca_cert.file_name, ovs_strerror(error)); unlink(ca_cert.file_name); return error; } VLOG_INFO("successfully bootstrapped CA cert to %s", ca_cert.file_name); log_ca_cert(ca_cert.file_name, cert); bootstrap_ca_cert = false; ca_cert.read = true; /* SSL_CTX_add_client_CA makes a copy of cert's relevant data. */ SSL_CTX_add_client_CA(ctx, cert); SSL_CTX_set_cert_store(ctx, X509_STORE_new()); if (SSL_CTX_load_verify_locations(ctx, ca_cert.file_name, NULL) != 1) { VLOG_ERR("SSL_CTX_load_verify_locations: %s", ERR_error_string(ERR_get_error(), NULL)); return EPROTO; } VLOG_INFO("killing successful connection to retry using CA cert"); return EPROTO; }
void jsonrpc_session_run(struct jsonrpc_session *s) { if (s->pstream) { struct stream *stream; int error; error = pstream_accept(s->pstream, &stream); if (!error) { if (s->rpc || s->stream) { VLOG_INFO_RL(&rl, "%s: new connection replacing active connection", reconnect_get_name(s->reconnect)); jsonrpc_session_disconnect(s); } reconnect_connected(s->reconnect, time_msec()); s->rpc = jsonrpc_open(stream); } else if (error != EAGAIN) { reconnect_listen_error(s->reconnect, time_msec(), error); pstream_close(s->pstream); s->pstream = NULL; } } if (s->rpc) { size_t backlog; int error; backlog = jsonrpc_get_backlog(s->rpc); jsonrpc_run(s->rpc); if (jsonrpc_get_backlog(s->rpc) < backlog) { /* Data previously caught in a queue was successfully sent (or * there's an error, which we'll catch below.) * * We don't count data that is successfully sent immediately as * activity, because there's a lot of queuing downstream from us, * which means that we can push a lot of data into a connection * that has stalled and won't ever recover. */ reconnect_activity(s->reconnect, time_msec()); } error = jsonrpc_get_status(s->rpc); if (error) { reconnect_disconnected(s->reconnect, time_msec(), error); jsonrpc_session_disconnect(s); s->last_error = error; } } else if (s->stream) { int error; stream_run(s->stream); error = stream_connect(s->stream); if (!error) { reconnect_connected(s->reconnect, time_msec()); s->rpc = jsonrpc_open(s->stream); s->stream = NULL; } else if (error != EAGAIN) { reconnect_connect_failed(s->reconnect, time_msec(), error); stream_close(s->stream); s->stream = NULL; s->last_error = error; } } switch (reconnect_run(s->reconnect, time_msec())) { case RECONNECT_CONNECT: jsonrpc_session_connect(s); break; case RECONNECT_DISCONNECT: reconnect_disconnected(s->reconnect, time_msec(), 0); jsonrpc_session_disconnect(s); break; case RECONNECT_PROBE: if (s->rpc) { struct json *params; struct jsonrpc_msg *request; params = json_array_create_empty(); request = jsonrpc_create_request("echo", params, NULL); json_destroy(request->id); request->id = json_string_create("echo"); jsonrpc_send(s->rpc, request); } break; } }
void jsonrpc_session_run(struct jsonrpc_session *s) { if (s->pstream) { struct stream *stream; int error; error = pstream_accept(s->pstream, &stream); if (!error) { if (s->rpc || s->stream) { VLOG_INFO_RL(&rl, "%s: new connection replacing active connection", reconnect_get_name(s->reconnect)); jsonrpc_session_disconnect(s); } reconnect_connected(s->reconnect, time_msec()); s->rpc = jsonrpc_open(stream); } else if (error != EAGAIN) { reconnect_listen_error(s->reconnect, time_msec(), error); pstream_close(s->pstream); s->pstream = NULL; } } if (s->rpc) { int error; jsonrpc_run(s->rpc); error = jsonrpc_get_status(s->rpc); if (error) { reconnect_disconnected(s->reconnect, time_msec(), error); jsonrpc_session_disconnect(s); } } else if (s->stream) { int error; stream_run(s->stream); error = stream_connect(s->stream); if (!error) { reconnect_connected(s->reconnect, time_msec()); s->rpc = jsonrpc_open(s->stream); s->stream = NULL; } else if (error != EAGAIN) { reconnect_connect_failed(s->reconnect, time_msec(), error); stream_close(s->stream); s->stream = NULL; } } switch (reconnect_run(s->reconnect, time_msec())) { case RECONNECT_CONNECT: jsonrpc_session_connect(s); break; case RECONNECT_DISCONNECT: reconnect_disconnected(s->reconnect, time_msec(), 0); jsonrpc_session_disconnect(s); break; case RECONNECT_PROBE: if (s->rpc) { struct json *params; struct jsonrpc_msg *request; params = json_array_create_empty(); request = jsonrpc_create_request("echo", params, NULL); json_destroy(request->id); request->id = json_string_create("echo"); jsonrpc_send(s->rpc, request); } break; } }
static int ofputil_pull_queue_get_config_reply10(struct ofpbuf *msg, struct ofputil_queue_config *queue) { const struct ofp_header *oh = msg->header; unsigned int opq_len; /* Length of protocol-specific queue header. */ unsigned int len; /* Total length of queue + properties. */ /* Obtain the port number from the message header. */ if (oh->version == OFP10_VERSION) { const struct ofp10_queue_get_config_reply *oqgcr10 = msg->msg; queue->port = u16_to_ofp(ntohs(oqgcr10->port)); } else { const struct ofp11_queue_get_config_reply *oqgcr11 = msg->msg; enum ofperr error = ofputil_port_from_ofp11(oqgcr11->port, &queue->port); if (error) { return error; } } /* Pull off the queue header and get the queue number and length. */ if (oh->version < OFP12_VERSION) { const struct ofp10_packet_queue *opq10; opq10 = ofpbuf_try_pull(msg, sizeof *opq10); if (!opq10) { return OFPERR_OFPBRC_BAD_LEN; } queue->queue = ntohl(opq10->queue_id); len = ntohs(opq10->len); opq_len = sizeof *opq10; } else { const struct ofp12_packet_queue *opq12; opq12 = ofpbuf_try_pull(msg, sizeof *opq12); if (!opq12) { return OFPERR_OFPBRC_BAD_LEN; } queue->queue = ntohl(opq12->queue_id); len = ntohs(opq12->len); opq_len = sizeof *opq12; } /* Length check. */ if (len < opq_len || len > msg->size + opq_len || len % 8) { return OFPERR_OFPBRC_BAD_LEN; } len -= opq_len; /* Pull properties. The format of these properties differs from used in * OF1.4+ so we can't use the common property functions. */ while (len > 0) { const struct ofp10_queue_prop_header *hdr; unsigned int property; unsigned int prop_len; enum ofperr error = 0; hdr = ofpbuf_at_assert(msg, 0, sizeof *hdr); prop_len = ntohs(hdr->len); if (prop_len < sizeof *hdr || prop_len > len || prop_len % 8) { return OFPERR_OFPBRC_BAD_LEN; } property = ntohs(hdr->property); switch (property) { case OFPQT10_MIN_RATE: error = parse_ofp10_queue_rate(hdr, &queue->min_rate); break; case OFPQT11_MAX_RATE: error = parse_ofp10_queue_rate(hdr, &queue->max_rate); break; default: VLOG_INFO_RL(&rl, "unknown queue property %u", property); break; } if (error) { return error; } ofpbuf_pull(msg, prop_len); len -= prop_len; } return 0; }