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