]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
67a4917b | 2 | * Copyright (c) 2008, 2009, 2010 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
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: | |
064af421 | 7 | * |
a14bc59f BP |
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. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "discovery.h" | |
19 | #include <errno.h> | |
20 | #include <inttypes.h> | |
9d82ec47 | 21 | #include <sys/socket.h> |
064af421 BP |
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" | |
fe55ad15 | 33 | #include "stream-ssl.h" |
064af421 BP |
34 | #include "vlog.h" |
35 | ||
5136ce49 BP |
36 | VLOG_DEFINE_THIS_MODULE(discovery) |
37 | ||
064af421 | 38 | struct discovery { |
c2e01f64 | 39 | char *dpif_name; |
064af421 BP |
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", | |
d295e8e9 | 63 | dhclient_get_state_elapsed(d->dhcp)); |
064af421 BP |
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 | ||
ec6fde61 | 107 | d = xzalloc(sizeof *d); |
064af421 | 108 | |
c2e01f64 BP |
109 | d->dpif_name = xstrdup(dpif_base_name(dpif)); |
110 | ||
064af421 BP |
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. */ | |
335562c0 BP |
119 | error = dpif_port_get_name(dpif, ODPP_LOCAL, |
120 | local_name, sizeof local_name); | |
064af421 | 121 | if (error) { |
c2e01f64 BP |
122 | VLOG_ERR("%s: failed to query datapath local port: %s", |
123 | d->dpif_name, strerror(error)); | |
064af421 BP |
124 | goto error_regfree; |
125 | } | |
126 | error = dhclient_create(local_name, modify_dhcp_request, | |
127 | validate_dhcp_offer, d, &d->dhcp); | |
128 | if (error) { | |
c2e01f64 BP |
129 | VLOG_ERR("%s: failed to initialize DHCP client: %s", |
130 | d->dpif_name, strerror(error)); | |
064af421 BP |
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: | |
c2e01f64 | 146 | free(d->dpif_name); |
064af421 BP |
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); | |
c2e01f64 | 161 | free(d->dpif_name); |
064af421 BP |
162 | free(d); |
163 | } | |
164 | } | |
165 | ||
79c9f2ee BP |
166 | bool |
167 | discovery_get_update_resolv_conf(const struct discovery *d) | |
168 | { | |
169 | return d->update_resolv_conf; | |
170 | } | |
171 | ||
064af421 BP |
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 | ||
79c9f2ee BP |
179 | const char * |
180 | discovery_get_accept_controller_re(const struct discovery *d) | |
181 | { | |
182 | return d->re; | |
183 | } | |
184 | ||
064af421 BP |
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 | ||
fe55ad15 | 192 | re = (!re_ ? xstrdup(stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*") |
064af421 BP |
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); | |
c2e01f64 | 200 | VLOG_WARN("%s: %s: %s", d->dpif_name, re, buffer); |
064af421 BP |
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) { | |
d295e8e9 | 221 | dhclient_force_renew(d->dhcp, 15); |
064af421 BP |
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); | |
c2e01f64 BP |
246 | VLOG_INFO("%s: discovered controller %s", |
247 | d->dpif_name, *controller_name); | |
064af421 BP |
248 | d->n_changes++; |
249 | } else { | |
250 | *controller_name = NULL; | |
251 | if (d->n_changes) { | |
c2e01f64 BP |
252 | VLOG_INFO("%s: discovered controller no longer available", |
253 | d->dpif_name); | |
064af421 BP |
254 | d->n_changes++; |
255 | } | |
256 | } | |
257 | return true; | |
258 | } | |
259 | ||
260 | void | |
261 | discovery_wait(struct discovery *d) | |
262 | { | |
263 | if (d->dhcp) { | |
d295e8e9 | 264 | dhclient_wait(d->dhcp); |
064af421 BP |
265 | } |
266 | } | |
267 | ||
268 | static void | |
67a4917b | 269 | modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED) |
064af421 BP |
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) { | |
c2e01f64 BP |
283 | VLOG_WARN_RL(&rl, "%s: rejecting DHCP offer missing controller vconn", |
284 | d->dpif_name); | |
064af421 BP |
285 | return false; |
286 | } | |
287 | accept = !regexec(d->regex, vconn_name, 0, NULL, 0); | |
288 | if (!accept) { | |
c2e01f64 BP |
289 | VLOG_WARN_RL(&rl, "%s: rejecting controller vconn that fails to " |
290 | "match %s", d->dpif_name, d->re); | |
064af421 BP |
291 | } |
292 | free(vconn_name); | |
293 | return accept; | |
294 | } |