ofproto: Get rid of archaic "switch status" OpenFlow extension.
[sliver-openvswitch.git] / ofproto / discovery.c
1 /*
2  * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #include "discovery.h"
19 #include <errno.h>
20 #include <inttypes.h>
21 #include <sys/socket.h>
22 #include <net/if.h>
23 #include <regex.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include "dhcp-client.h"
27 #include "dhcp.h"
28 #include "dpif.h"
29 #include "netdev.h"
30 #include "openflow/openflow.h"
31 #include "packets.h"
32 #include "stream-ssl.h"
33 #include "vlog.h"
34
35 VLOG_DEFINE_THIS_MODULE(discovery);
36
37 struct discovery {
38     char *dpif_name;
39     char *re;
40     bool update_resolv_conf;
41     regex_t *regex;
42     struct dhclient *dhcp;
43     int n_changes;
44 };
45
46 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
47 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
48
49 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
50
51 int
52 discovery_create(const char *re, bool update_resolv_conf,
53                  struct dpif *dpif, struct discovery **discoveryp)
54 {
55     struct discovery *d;
56     char local_name[IF_NAMESIZE];
57     int error;
58
59     d = xzalloc(sizeof *d);
60
61     d->dpif_name = xstrdup(dpif_base_name(dpif));
62
63     /* Controller regular expression. */
64     error = discovery_set_accept_controller_re(d, re);
65     if (error) {
66         goto error_free;
67     }
68     d->update_resolv_conf = update_resolv_conf;
69
70     /* Initialize DHCP client. */
71     error = dpif_port_get_name(dpif, ODPP_LOCAL,
72                                local_name, sizeof local_name);
73     if (error) {
74         VLOG_ERR("%s: failed to query datapath local port: %s",
75                  d->dpif_name, strerror(error));
76         goto error_regfree;
77     }
78     error = dhclient_create(local_name, modify_dhcp_request,
79                             validate_dhcp_offer, d, &d->dhcp);
80     if (error) {
81         VLOG_ERR("%s: failed to initialize DHCP client: %s",
82                  d->dpif_name, strerror(error));
83         goto error_regfree;
84     }
85     dhclient_set_max_timeout(d->dhcp, 3);
86     dhclient_init(d->dhcp, 0);
87
88     *discoveryp = d;
89     return 0;
90
91 error_regfree:
92     regfree(d->regex);
93     free(d->regex);
94 error_free:
95     free(d->dpif_name);
96     free(d);
97     *discoveryp = 0;
98     return error;
99 }
100
101 void
102 discovery_destroy(struct discovery *d)
103 {
104     if (d) {
105         free(d->re);
106         regfree(d->regex);
107         free(d->regex);
108         dhclient_destroy(d->dhcp);
109         free(d->dpif_name);
110         free(d);
111     }
112 }
113
114 bool
115 discovery_get_update_resolv_conf(const struct discovery *d)
116 {
117     return d->update_resolv_conf;
118 }
119
120 void
121 discovery_set_update_resolv_conf(struct discovery *d,
122                                  bool update_resolv_conf)
123 {
124     d->update_resolv_conf = update_resolv_conf;
125 }
126
127 const char *
128 discovery_get_accept_controller_re(const struct discovery *d)
129 {
130     return d->re;
131 }
132
133 int
134 discovery_set_accept_controller_re(struct discovery *d, const char *re_)
135 {
136     regex_t *regex;
137     int error;
138     char *re;
139
140     re = (!re_ ? xstrdup(stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*")
141           : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_));
142     regex = xmalloc(sizeof *regex);
143     error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED);
144     if (error) {
145         size_t length = regerror(error, regex, NULL, 0);
146         char *buffer = xmalloc(length);
147         regerror(error, regex, buffer, length);
148         VLOG_WARN("%s: %s: %s", d->dpif_name, re, buffer);
149         free(buffer);
150         free(regex);
151         free(re);
152         return EINVAL;
153     } else {
154         if (d->regex) {
155             regfree(d->regex);
156             free(d->regex);
157         }
158         free(d->re);
159
160         d->regex = regex;
161         d->re = re;
162         return 0;
163     }
164 }
165
166 void
167 discovery_question_connectivity(struct discovery *d)
168 {
169     if (d->dhcp) {
170         dhclient_force_renew(d->dhcp, 15);
171     }
172 }
173
174 bool
175 discovery_run(struct discovery *d, char **controller_name)
176 {
177     if (!d->dhcp) {
178         *controller_name = NULL;
179         return true;
180     }
181
182     dhclient_run(d->dhcp);
183     if (!dhclient_changed(d->dhcp)) {
184         return false;
185     }
186
187     dhclient_configure_netdev(d->dhcp);
188     if (d->update_resolv_conf) {
189         dhclient_update_resolv_conf(d->dhcp);
190     }
191
192     if (dhclient_is_bound(d->dhcp)) {
193         *controller_name = dhcp_msg_get_string(dhclient_get_config(d->dhcp),
194                                                DHCP_CODE_OFP_CONTROLLER_VCONN);
195         VLOG_INFO("%s: discovered controller %s",
196                   d->dpif_name, *controller_name);
197         d->n_changes++;
198     } else {
199         *controller_name = NULL;
200         if (d->n_changes) {
201             VLOG_INFO("%s: discovered controller no longer available",
202                       d->dpif_name);
203             d->n_changes++;
204         }
205     }
206     return true;
207 }
208
209 void
210 discovery_wait(struct discovery *d)
211 {
212     if (d->dhcp) {
213         dhclient_wait(d->dhcp);
214     }
215 }
216
217 static void
218 modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED)
219 {
220     dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
221 }
222
223 static bool
224 validate_dhcp_offer(const struct dhcp_msg *msg, void *d_)
225 {
226     const struct discovery *d = d_;
227     char *vconn_name;
228     bool accept;
229
230     vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
231     if (!vconn_name) {
232         VLOG_WARN_RL(&rl, "%s: rejecting DHCP offer missing controller vconn",
233                      d->dpif_name);
234         return false;
235     }
236     accept = !regexec(d->regex, vconn_name, 0, NULL, 0);
237     if (!accept) {
238         VLOG_WARN_RL(&rl, "%s: rejecting controller vconn that fails to "
239                      "match %s", d->dpif_name, d->re);
240     }
241     free(vconn_name);
242     return accept;
243 }