diff options
Diffstat (limited to 'secchan/discovery.c')
-rw-r--r-- | secchan/discovery.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/secchan/discovery.c b/secchan/discovery.c new file mode 100644 index 000000000..06de6f07c --- /dev/null +++ b/secchan/discovery.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2008, 2009 Nicira Networks. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include "discovery.h" +#include <errno.h> +#include <inttypes.h> +#include <net/if.h> +#include <regex.h> +#include <stdlib.h> +#include <string.h> +#include "dhcp-client.h" +#include "dhcp.h" +#include "dpif.h" +#include "netdev.h" +#include "openflow/openflow.h" +#include "packets.h" +#include "status.h" +#include "vconn-ssl.h" + +#define THIS_MODULE VLM_discovery +#include "vlog.h" + +struct discovery { + char *re; + bool update_resolv_conf; + regex_t *regex; + struct dhclient *dhcp; + int n_changes; + struct status_category *ss_cat; +}; + +static void modify_dhcp_request(struct dhcp_msg *, void *aux); +static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux); + +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); + +static void +discovery_status_cb(struct status_reply *sr, void *d_) +{ + struct discovery *d = d_; + + status_reply_put(sr, "accept-remote=%s", d->re); + status_reply_put(sr, "n-changes=%d", d->n_changes); + if (d->dhcp) { + status_reply_put(sr, "state=%s", dhclient_get_state(d->dhcp)); + status_reply_put(sr, "state-elapsed=%u", + dhclient_get_state_elapsed(d->dhcp)); + if (dhclient_is_bound(d->dhcp)) { + uint32_t ip = dhclient_get_ip(d->dhcp); + uint32_t netmask = dhclient_get_netmask(d->dhcp); + uint32_t router = dhclient_get_router(d->dhcp); + + const struct dhcp_msg *cfg = dhclient_get_config(d->dhcp); + uint32_t dns_server; + char *domain_name; + int i; + + status_reply_put(sr, "ip="IP_FMT, IP_ARGS(&ip)); + status_reply_put(sr, "netmask="IP_FMT, IP_ARGS(&netmask)); + if (router) { + status_reply_put(sr, "router="IP_FMT, IP_ARGS(&router)); + } + + for (i = 0; dhcp_msg_get_ip(cfg, DHCP_CODE_DNS_SERVER, i, + &dns_server); + i++) { + status_reply_put(sr, "dns%d="IP_FMT, i, IP_ARGS(&dns_server)); + } + + domain_name = dhcp_msg_get_string(cfg, DHCP_CODE_DOMAIN_NAME); + if (domain_name) { + status_reply_put(sr, "domain=%s", domain_name); + free(domain_name); + } + + status_reply_put(sr, "lease-remaining=%u", + dhclient_get_lease_remaining(d->dhcp)); + } + } +} + +int +discovery_create(const char *re, bool update_resolv_conf, + struct dpif *dpif, struct switch_status *ss, + struct discovery **discoveryp) +{ + struct discovery *d; + char local_name[IF_NAMESIZE]; + int error; + + d = xcalloc(1, sizeof *d); + + /* Controller regular expression. */ + error = discovery_set_accept_controller_re(d, re); + if (error) { + goto error_free; + } + d->update_resolv_conf = update_resolv_conf; + + /* Initialize DHCP client. */ + error = dpif_get_name(dpif, local_name, sizeof local_name); + if (error) { + VLOG_ERR("failed to query datapath local port: %s", strerror(error)); + goto error_regfree; + } + error = dhclient_create(local_name, modify_dhcp_request, + validate_dhcp_offer, d, &d->dhcp); + if (error) { + VLOG_ERR("failed to initialize DHCP client: %s", strerror(error)); + goto error_regfree; + } + dhclient_set_max_timeout(d->dhcp, 3); + dhclient_init(d->dhcp, 0); + + d->ss_cat = switch_status_register(ss, "discovery", + discovery_status_cb, d); + + *discoveryp = d; + return 0; + +error_regfree: + regfree(d->regex); + free(d->regex); +error_free: + free(d); + *discoveryp = 0; + return error; +} + +void +discovery_destroy(struct discovery *d) +{ + if (d) { + free(d->re); + regfree(d->regex); + free(d->regex); + dhclient_destroy(d->dhcp); + switch_status_unregister(d->ss_cat); + free(d); + } +} + +void +discovery_set_update_resolv_conf(struct discovery *d, + bool update_resolv_conf) +{ + d->update_resolv_conf = update_resolv_conf; +} + +int +discovery_set_accept_controller_re(struct discovery *d, const char *re_) +{ + regex_t *regex; + int error; + char *re; + + re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : ".*") + : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_)); + regex = xmalloc(sizeof *regex); + error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED); + if (error) { + size_t length = regerror(error, regex, NULL, 0); + char *buffer = xmalloc(length); + regerror(error, regex, buffer, length); + VLOG_WARN("%s: %s", re, buffer); + free(regex); + free(re); + return EINVAL; + } else { + if (d->regex) { + regfree(d->regex); + free(d->regex); + } + free(d->re); + + d->regex = regex; + d->re = re; + return 0; + } +} + +void +discovery_question_connectivity(struct discovery *d) +{ + if (d->dhcp) { + dhclient_force_renew(d->dhcp, 15); + } +} + +bool +discovery_run(struct discovery *d, char **controller_name) +{ + if (!d->dhcp) { + *controller_name = NULL; + return true; + } + + dhclient_run(d->dhcp); + if (!dhclient_changed(d->dhcp)) { + return false; + } + + dhclient_configure_netdev(d->dhcp); + if (d->update_resolv_conf) { + dhclient_update_resolv_conf(d->dhcp); + } + + if (dhclient_is_bound(d->dhcp)) { + *controller_name = dhcp_msg_get_string(dhclient_get_config(d->dhcp), + DHCP_CODE_OFP_CONTROLLER_VCONN); + VLOG_INFO("%s: discovered controller", *controller_name); + d->n_changes++; + } else { + *controller_name = NULL; + if (d->n_changes) { + VLOG_INFO("discovered controller no longer available"); + d->n_changes++; + } + } + return true; +} + +void +discovery_wait(struct discovery *d) +{ + if (d->dhcp) { + dhclient_wait(d->dhcp); + } +} + +static void +modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED) +{ + dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow"); +} + +static bool +validate_dhcp_offer(const struct dhcp_msg *msg, void *d_) +{ + const struct discovery *d = d_; + char *vconn_name; + bool accept; + + vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN); + if (!vconn_name) { + VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn"); + return false; + } + accept = !regexec(d->regex, vconn_name, 0, NULL, 0); + if (!accept) { + VLOG_WARN_RL(&rl, "rejecting controller vconn that fails to match %s", + d->re); + } + free(vconn_name); + return accept; +} |