ofp-util: Use our usual variable naming convention in make_ofp_error_msg().
[sliver-openvswitch.git] / ofproto / discovery.c
1 /*
2  * Copyright (c) 2008, 2009, 2010 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 "netdev.h"
29 #include "openflow/openflow.h"
30 #include "packets.h"
31 #include "status.h"
32 #include "stream-ssl.h"
33 #include "vlog.h"
34 #include "wdp.h"
35 #include "xfif.h"
36
37 VLOG_DEFINE_THIS_MODULE(discovery)
38
39 struct discovery {
40     char *dpif_name;
41     char *re;
42     bool update_resolv_conf;
43     regex_t *regex;
44     struct dhclient *dhcp;
45     int n_changes;
46     struct status_category *ss_cat;
47 };
48
49 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
50 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
51
52 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
53
54 static void
55 discovery_status_cb(struct status_reply *sr, void *d_)
56 {
57     struct discovery *d = d_;
58
59     status_reply_put(sr, "accept-remote=%s", d->re);
60     status_reply_put(sr, "n-changes=%d", d->n_changes);
61     if (d->dhcp) {
62         status_reply_put(sr, "state=%s", dhclient_get_state(d->dhcp));
63         status_reply_put(sr, "state-elapsed=%u",
64                          dhclient_get_state_elapsed(d->dhcp));
65         if (dhclient_is_bound(d->dhcp)) {
66             uint32_t ip = dhclient_get_ip(d->dhcp);
67             uint32_t netmask = dhclient_get_netmask(d->dhcp);
68             uint32_t router = dhclient_get_router(d->dhcp);
69
70             const struct dhcp_msg *cfg = dhclient_get_config(d->dhcp);
71             uint32_t dns_server;
72             char *domain_name;
73             int i;
74
75             status_reply_put(sr, "ip="IP_FMT, IP_ARGS(&ip));
76             status_reply_put(sr, "netmask="IP_FMT, IP_ARGS(&netmask));
77             if (router) {
78                 status_reply_put(sr, "router="IP_FMT, IP_ARGS(&router));
79             }
80
81             for (i = 0; dhcp_msg_get_ip(cfg, DHCP_CODE_DNS_SERVER, i,
82                                         &dns_server);
83                  i++) {
84                 status_reply_put(sr, "dns%d="IP_FMT, i, IP_ARGS(&dns_server));
85             }
86
87             domain_name = dhcp_msg_get_string(cfg, DHCP_CODE_DOMAIN_NAME);
88             if (domain_name) {
89                 status_reply_put(sr, "domain=%s", domain_name);
90                 free(domain_name);
91             }
92
93             status_reply_put(sr, "lease-remaining=%u",
94                              dhclient_get_lease_remaining(d->dhcp));
95         }
96     }
97 }
98
99 int
100 discovery_create(const char *re, bool update_resolv_conf,
101                  struct wdp *wdp, struct switch_status *ss,
102                  struct discovery **discoveryp)
103 {
104     struct discovery *d;
105     char *local_name;
106     int error;
107
108     d = xzalloc(sizeof *d);
109
110     d->dpif_name = xstrdup(wdp_base_name(wdp));
111
112     /* Controller regular expression. */
113     error = discovery_set_accept_controller_re(d, re);
114     if (error) {
115         goto error_free;
116     }
117     d->update_resolv_conf = update_resolv_conf;
118
119     /* Initialize DHCP client. */
120     error = wdp_port_get_name(wdp, OFPP_LOCAL, &local_name);
121     if (error) {
122         VLOG_ERR("%s: failed to query datapath local port: %s",
123                  d->dpif_name, strerror(error));
124         goto error_regfree;
125     }
126     error = dhclient_create(local_name, modify_dhcp_request,
127                             validate_dhcp_offer, d, &d->dhcp);
128     free(local_name);
129     if (error) {
130         VLOG_ERR("%s: failed to initialize DHCP client: %s",
131                  d->dpif_name, strerror(error));
132         goto error_regfree;
133     }
134     dhclient_set_max_timeout(d->dhcp, 3);
135     dhclient_init(d->dhcp, 0);
136
137     d->ss_cat = switch_status_register(ss, "discovery",
138                                        discovery_status_cb, d);
139
140     *discoveryp = d;
141     return 0;
142
143 error_regfree:
144     regfree(d->regex);
145     free(d->regex);
146 error_free:
147     free(d->dpif_name);
148     free(d);
149     *discoveryp = 0;
150     return error;
151 }
152
153 void
154 discovery_destroy(struct discovery *d)
155 {
156     if (d) {
157         free(d->re);
158         regfree(d->regex);
159         free(d->regex);
160         dhclient_destroy(d->dhcp);
161         switch_status_unregister(d->ss_cat);
162         free(d->dpif_name);
163         free(d);
164     }
165 }
166
167 bool
168 discovery_get_update_resolv_conf(const struct discovery *d)
169 {
170     return d->update_resolv_conf;
171 }
172
173 void
174 discovery_set_update_resolv_conf(struct discovery *d,
175                                  bool update_resolv_conf)
176 {
177     d->update_resolv_conf = update_resolv_conf;
178 }
179
180 const char *
181 discovery_get_accept_controller_re(const struct discovery *d)
182 {
183     return d->re;
184 }
185
186 int
187 discovery_set_accept_controller_re(struct discovery *d, const char *re_)
188 {
189     regex_t *regex;
190     int error;
191     char *re;
192
193     re = (!re_ ? xstrdup(stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*")
194           : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_));
195     regex = xmalloc(sizeof *regex);
196     error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED);
197     if (error) {
198         size_t length = regerror(error, regex, NULL, 0);
199         char *buffer = xmalloc(length);
200         regerror(error, regex, buffer, length);
201         VLOG_WARN("%s: %s: %s", d->dpif_name, re, buffer);
202         free(regex);
203         free(re);
204         return EINVAL;
205     } else {
206         if (d->regex) {
207             regfree(d->regex);
208             free(d->regex);
209         }
210         free(d->re);
211
212         d->regex = regex;
213         d->re = re;
214         return 0;
215     }
216 }
217
218 void
219 discovery_question_connectivity(struct discovery *d)
220 {
221     if (d->dhcp) {
222         dhclient_force_renew(d->dhcp, 15);
223     }
224 }
225
226 bool
227 discovery_run(struct discovery *d, char **controller_name)
228 {
229     if (!d->dhcp) {
230         *controller_name = NULL;
231         return true;
232     }
233
234     dhclient_run(d->dhcp);
235     if (!dhclient_changed(d->dhcp)) {
236         return false;
237     }
238
239     dhclient_configure_netdev(d->dhcp);
240     if (d->update_resolv_conf) {
241         dhclient_update_resolv_conf(d->dhcp);
242     }
243
244     if (dhclient_is_bound(d->dhcp)) {
245         *controller_name = dhcp_msg_get_string(dhclient_get_config(d->dhcp),
246                                                DHCP_CODE_OFP_CONTROLLER_VCONN);
247         VLOG_INFO("%s: discovered controller %s",
248                   d->dpif_name, *controller_name);
249         d->n_changes++;
250     } else {
251         *controller_name = NULL;
252         if (d->n_changes) {
253             VLOG_INFO("%s: discovered controller no longer available",
254                       d->dpif_name);
255             d->n_changes++;
256         }
257     }
258     return true;
259 }
260
261 void
262 discovery_wait(struct discovery *d)
263 {
264     if (d->dhcp) {
265         dhclient_wait(d->dhcp);
266     }
267 }
268
269 static void
270 modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED)
271 {
272     dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
273 }
274
275 static bool
276 validate_dhcp_offer(const struct dhcp_msg *msg, void *d_)
277 {
278     const struct discovery *d = d_;
279     char *vconn_name;
280     bool accept;
281
282     vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
283     if (!vconn_name) {
284         VLOG_WARN_RL(&rl, "%s: rejecting DHCP offer missing controller vconn",
285                      d->dpif_name);
286         return false;
287     }
288     accept = !regexec(d->regex, vconn_name, 0, NULL, 0);
289     if (!accept) {
290         VLOG_WARN_RL(&rl, "%s: rejecting controller vconn that fails to "
291                      "match %s", d->dpif_name, d->re);
292     }
293     free(vconn_name);
294     return accept;
295 }