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