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