]>
Commit | Line | Data |
---|---|---|
96fba48f | 1 | /* |
1a6f1e2a | 2 | * Copyright (c) 2008, 2009, 2010 Nicira Networks. |
96fba48f BP |
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 "dpif.h" | |
19 | ||
20 | #include <assert.h> | |
21 | #include <ctype.h> | |
22 | #include <errno.h> | |
23 | #include <fcntl.h> | |
24 | #include <inttypes.h> | |
25 | #include <net/if.h> | |
26 | #include <linux/ethtool.h> | |
e9e28be3 | 27 | #include <linux/rtnetlink.h> |
96fba48f BP |
28 | #include <linux/sockios.h> |
29 | #include <stdlib.h> | |
30 | #include <sys/ioctl.h> | |
31 | #include <unistd.h> | |
32 | ||
33 | #include "dpif-provider.h" | |
3abc4a1a | 34 | #include "netdev.h" |
96fba48f BP |
35 | #include "ofpbuf.h" |
36 | #include "poll-loop.h" | |
559843ed | 37 | #include "rtnetlink.h" |
54825e09 | 38 | #include "shash.h" |
e9e28be3 | 39 | #include "svec.h" |
96fba48f BP |
40 | #include "util.h" |
41 | ||
42 | #include "vlog.h" | |
43 | #define THIS_MODULE VLM_dpif_linux | |
44 | ||
45 | /* Datapath interface for the openvswitch Linux kernel module. */ | |
46 | struct dpif_linux { | |
47 | struct dpif dpif; | |
48 | int fd; | |
e9e28be3 | 49 | |
d3d22744 BP |
50 | /* Used by dpif_linux_get_all_names(). */ |
51 | char *local_ifname; | |
52 | int minor; | |
53 | ||
e9e28be3 BP |
54 | /* Change notification. */ |
55 | int local_ifindex; /* Ifindex of local port. */ | |
54825e09 | 56 | struct shash changed_ports; /* Ports that have changed. */ |
46097491 | 57 | struct rtnetlink_notifier port_notifier; |
8b61709d | 58 | bool change_error; |
96fba48f BP |
59 | }; |
60 | ||
61 | static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5); | |
62 | ||
63 | static int do_ioctl(const struct dpif *, int cmd, const void *arg); | |
64 | static int lookup_minor(const char *name, int *minor); | |
e9e28be3 | 65 | static int finish_open(struct dpif *, const char *local_ifname); |
57aaff8a | 66 | static int get_openvswitch_major(void); |
96fba48f BP |
67 | static int create_minor(const char *name, int minor, struct dpif **dpifp); |
68 | static int open_minor(int minor, struct dpif **dpifp); | |
69 | static int make_openvswitch_device(int minor, char **fnp); | |
46097491 | 70 | static void dpif_linux_port_changed(const struct rtnetlink_change *, |
e9e28be3 | 71 | void *dpif); |
96fba48f BP |
72 | |
73 | static struct dpif_linux * | |
74 | dpif_linux_cast(const struct dpif *dpif) | |
75 | { | |
76 | dpif_assert_class(dpif, &dpif_linux_class); | |
77 | return CONTAINER_OF(dpif, struct dpif_linux, dpif); | |
78 | } | |
79 | ||
d3d22744 BP |
80 | static int |
81 | dpif_linux_enumerate(struct svec *all_dps) | |
82 | { | |
57aaff8a | 83 | int major; |
d3d22744 BP |
84 | int error; |
85 | int i; | |
86 | ||
57aaff8a JP |
87 | /* Check that the Open vSwitch module is loaded. */ |
88 | major = get_openvswitch_major(); | |
89 | if (major < 0) { | |
90 | return -major; | |
91 | } | |
92 | ||
d3d22744 BP |
93 | error = 0; |
94 | for (i = 0; i < ODP_MAX; i++) { | |
95 | struct dpif *dpif; | |
96 | char devname[16]; | |
97 | int retval; | |
98 | ||
99 | sprintf(devname, "dp%d", i); | |
1a6f1e2a | 100 | retval = dpif_open(devname, "system", &dpif); |
d3d22744 BP |
101 | if (!retval) { |
102 | svec_add(all_dps, devname); | |
999401aa | 103 | dpif_uninit(dpif, true); |
d3d22744 BP |
104 | } else if (retval != ENODEV && !error) { |
105 | error = retval; | |
106 | } | |
107 | } | |
108 | return error; | |
109 | } | |
110 | ||
96fba48f | 111 | static int |
c69ee87c | 112 | dpif_linux_open(const char *name, const char *type OVS_UNUSED, bool create, |
96fba48f BP |
113 | struct dpif **dpifp) |
114 | { | |
115 | int minor; | |
116 | ||
be2c418b JP |
117 | minor = !strncmp(name, "dp", 2) |
118 | && isdigit((unsigned char)name[2]) ? atoi(name + 2) : -1; | |
96fba48f BP |
119 | if (create) { |
120 | if (minor >= 0) { | |
1a6f1e2a | 121 | return create_minor(name, minor, dpifp); |
96fba48f BP |
122 | } else { |
123 | /* Scan for unused minor number. */ | |
124 | for (minor = 0; minor < ODP_MAX; minor++) { | |
1a6f1e2a | 125 | int error = create_minor(name, minor, dpifp); |
96fba48f BP |
126 | if (error != EBUSY) { |
127 | return error; | |
128 | } | |
129 | } | |
130 | ||
131 | /* All datapath numbers in use. */ | |
132 | return ENOBUFS; | |
133 | } | |
134 | } else { | |
135 | struct dpif_linux *dpif; | |
e9e28be3 | 136 | struct odp_port port; |
96fba48f BP |
137 | int error; |
138 | ||
139 | if (minor < 0) { | |
1a6f1e2a | 140 | error = lookup_minor(name, &minor); |
96fba48f BP |
141 | if (error) { |
142 | return error; | |
143 | } | |
144 | } | |
145 | ||
146 | error = open_minor(minor, dpifp); | |
147 | if (error) { | |
148 | return error; | |
149 | } | |
150 | dpif = dpif_linux_cast(*dpifp); | |
151 | ||
e9e28be3 BP |
152 | /* We need the local port's ifindex for the poll function. Start by |
153 | * getting the local port's name. */ | |
154 | memset(&port, 0, sizeof port); | |
155 | port.port = ODPP_LOCAL; | |
156 | if (ioctl(dpif->fd, ODP_PORT_QUERY, &port)) { | |
157 | error = errno; | |
96fba48f BP |
158 | if (error != ENODEV) { |
159 | VLOG_WARN("%s: probe returned unexpected error: %s", | |
160 | dpif_name(*dpifp), strerror(error)); | |
161 | } | |
999401aa | 162 | dpif_uninit(*dpifp, true); |
e9e28be3 | 163 | return error; |
96fba48f | 164 | } |
e9e28be3 BP |
165 | |
166 | /* Then use that to finish up opening. */ | |
167 | return finish_open(&dpif->dpif, port.devname); | |
96fba48f BP |
168 | } |
169 | } | |
170 | ||
171 | static void | |
172 | dpif_linux_close(struct dpif *dpif_) | |
173 | { | |
174 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
46097491 | 175 | rtnetlink_notifier_unregister(&dpif->port_notifier); |
54825e09 | 176 | shash_destroy(&dpif->changed_ports); |
d3d22744 | 177 | free(dpif->local_ifname); |
96fba48f BP |
178 | close(dpif->fd); |
179 | free(dpif); | |
180 | } | |
181 | ||
d3d22744 BP |
182 | static int |
183 | dpif_linux_get_all_names(const struct dpif *dpif_, struct svec *all_names) | |
184 | { | |
185 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
186 | ||
187 | svec_add_nocopy(all_names, xasprintf("dp%d", dpif->minor)); | |
188 | svec_add(all_names, dpif->local_ifname); | |
189 | return 0; | |
190 | } | |
191 | ||
96fba48f | 192 | static int |
7dab847a | 193 | dpif_linux_destroy(struct dpif *dpif_) |
96fba48f | 194 | { |
3abc4a1a JG |
195 | struct odp_port *ports; |
196 | size_t n_ports; | |
197 | int err; | |
198 | int i; | |
199 | ||
200 | err = dpif_port_list(dpif_, &ports, &n_ports); | |
201 | if (err) { | |
202 | return err; | |
203 | } | |
204 | ||
205 | for (i = 0; i < n_ports; i++) { | |
206 | if (ports[i].port != ODPP_LOCAL) { | |
207 | err = do_ioctl(dpif_, ODP_VPORT_DEL, ports[i].devname); | |
208 | if (err) { | |
209 | VLOG_WARN_RL(&error_rl, "%s: error deleting port %s (%s)", | |
210 | dpif_name(dpif_), ports[i].devname, strerror(err)); | |
211 | } | |
212 | } | |
213 | } | |
214 | ||
215 | free(ports); | |
216 | ||
96fba48f BP |
217 | return do_ioctl(dpif_, ODP_DP_DESTROY, NULL); |
218 | } | |
219 | ||
220 | static int | |
221 | dpif_linux_get_stats(const struct dpif *dpif_, struct odp_stats *stats) | |
222 | { | |
72b06300 | 223 | memset(stats, 0, sizeof *stats); |
96fba48f BP |
224 | return do_ioctl(dpif_, ODP_DP_STATS, stats); |
225 | } | |
226 | ||
227 | static int | |
228 | dpif_linux_get_drop_frags(const struct dpif *dpif_, bool *drop_fragsp) | |
229 | { | |
230 | int drop_frags; | |
231 | int error; | |
232 | ||
233 | error = do_ioctl(dpif_, ODP_GET_DROP_FRAGS, &drop_frags); | |
234 | if (!error) { | |
235 | *drop_fragsp = drop_frags & 1; | |
236 | } | |
237 | return error; | |
238 | } | |
239 | ||
240 | static int | |
241 | dpif_linux_set_drop_frags(struct dpif *dpif_, bool drop_frags) | |
242 | { | |
243 | int drop_frags_int = drop_frags; | |
244 | return do_ioctl(dpif_, ODP_SET_DROP_FRAGS, &drop_frags_int); | |
245 | } | |
246 | ||
247 | static int | |
248 | dpif_linux_port_add(struct dpif *dpif_, const char *devname, uint16_t flags, | |
249 | uint16_t *port_no) | |
250 | { | |
251 | struct odp_port port; | |
252 | int error; | |
253 | ||
254 | memset(&port, 0, sizeof port); | |
255 | strncpy(port.devname, devname, sizeof port.devname); | |
256 | port.flags = flags; | |
f2459fe7 | 257 | error = do_ioctl(dpif_, ODP_PORT_ATTACH, &port); |
96fba48f BP |
258 | if (!error) { |
259 | *port_no = port.port; | |
260 | } | |
261 | return error; | |
262 | } | |
263 | ||
264 | static int | |
265 | dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no) | |
266 | { | |
267 | int tmp = port_no; | |
3abc4a1a JG |
268 | int err; |
269 | struct odp_port port; | |
270 | ||
271 | err = dpif_port_query_by_number(dpif_, port_no, &port); | |
272 | if (err) { | |
273 | return err; | |
274 | } | |
275 | ||
276 | err = do_ioctl(dpif_, ODP_PORT_DETACH, &tmp); | |
277 | if (err) { | |
278 | return err; | |
279 | } | |
280 | ||
281 | if (!netdev_is_open(port.devname)) { | |
282 | /* Try deleting the port if no one has it open. This shouldn't | |
283 | * actually be necessary unless the config changed while we weren't | |
284 | * running but it won't hurt anything if the port is already gone. */ | |
285 | do_ioctl(dpif_, ODP_VPORT_DEL, port.devname); | |
286 | } | |
287 | ||
288 | return 0; | |
96fba48f BP |
289 | } |
290 | ||
291 | static int | |
292 | dpif_linux_port_query_by_number(const struct dpif *dpif_, uint16_t port_no, | |
293 | struct odp_port *port) | |
294 | { | |
295 | memset(port, 0, sizeof *port); | |
296 | port->port = port_no; | |
297 | return do_ioctl(dpif_, ODP_PORT_QUERY, port); | |
298 | } | |
299 | ||
300 | static int | |
301 | dpif_linux_port_query_by_name(const struct dpif *dpif_, const char *devname, | |
302 | struct odp_port *port) | |
303 | { | |
304 | memset(port, 0, sizeof *port); | |
305 | strncpy(port->devname, devname, sizeof port->devname); | |
306 | return do_ioctl(dpif_, ODP_PORT_QUERY, port); | |
307 | } | |
308 | ||
309 | static int | |
310 | dpif_linux_flow_flush(struct dpif *dpif_) | |
311 | { | |
312 | return do_ioctl(dpif_, ODP_FLOW_FLUSH, NULL); | |
313 | } | |
314 | ||
315 | static int | |
316 | dpif_linux_port_list(const struct dpif *dpif_, struct odp_port *ports, int n) | |
317 | { | |
318 | struct odp_portvec pv; | |
319 | int error; | |
320 | ||
321 | pv.ports = ports; | |
322 | pv.n_ports = n; | |
323 | error = do_ioctl(dpif_, ODP_PORT_LIST, &pv); | |
324 | return error ? -error : pv.n_ports; | |
325 | } | |
326 | ||
e9e28be3 BP |
327 | static int |
328 | dpif_linux_port_poll(const struct dpif *dpif_, char **devnamep) | |
329 | { | |
330 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
e9e28be3 | 331 | |
8b61709d BP |
332 | if (dpif->change_error) { |
333 | dpif->change_error = false; | |
54825e09 | 334 | shash_clear(&dpif->changed_ports); |
8b61709d | 335 | return ENOBUFS; |
54825e09 BP |
336 | } else if (!shash_is_empty(&dpif->changed_ports)) { |
337 | struct shash_node *node = shash_first(&dpif->changed_ports); | |
338 | *devnamep = xstrdup(node->name); | |
339 | shash_delete(&dpif->changed_ports, node); | |
8b61709d | 340 | return 0; |
e9e28be3 | 341 | } else { |
8b61709d | 342 | return EAGAIN; |
e9e28be3 | 343 | } |
e9e28be3 BP |
344 | } |
345 | ||
346 | static void | |
347 | dpif_linux_port_poll_wait(const struct dpif *dpif_) | |
348 | { | |
349 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
54825e09 | 350 | if (!shash_is_empty(&dpif->changed_ports) || dpif->change_error) { |
e9e28be3 BP |
351 | poll_immediate_wake(); |
352 | } else { | |
46097491 | 353 | rtnetlink_notifier_wait(); |
e9e28be3 BP |
354 | } |
355 | } | |
356 | ||
96fba48f BP |
357 | static int |
358 | dpif_linux_port_group_get(const struct dpif *dpif_, int group, | |
359 | uint16_t ports[], int n) | |
360 | { | |
361 | struct odp_port_group pg; | |
362 | int error; | |
363 | ||
364 | assert(n <= UINT16_MAX); | |
365 | pg.group = group; | |
366 | pg.ports = ports; | |
367 | pg.n_ports = n; | |
368 | error = do_ioctl(dpif_, ODP_PORT_GROUP_GET, &pg); | |
369 | return error ? -error : pg.n_ports; | |
370 | } | |
371 | ||
372 | static int | |
373 | dpif_linux_port_group_set(struct dpif *dpif_, int group, | |
374 | const uint16_t ports[], int n) | |
375 | { | |
376 | struct odp_port_group pg; | |
377 | ||
378 | assert(n <= UINT16_MAX); | |
379 | pg.group = group; | |
380 | pg.ports = (uint16_t *) ports; | |
381 | pg.n_ports = n; | |
382 | return do_ioctl(dpif_, ODP_PORT_GROUP_SET, &pg); | |
383 | } | |
384 | ||
385 | static int | |
386 | dpif_linux_flow_get(const struct dpif *dpif_, struct odp_flow flows[], int n) | |
387 | { | |
388 | struct odp_flowvec fv; | |
389 | fv.flows = flows; | |
390 | fv.n_flows = n; | |
391 | return do_ioctl(dpif_, ODP_FLOW_GET, &fv); | |
392 | } | |
393 | ||
394 | static int | |
395 | dpif_linux_flow_put(struct dpif *dpif_, struct odp_flow_put *put) | |
396 | { | |
397 | return do_ioctl(dpif_, ODP_FLOW_PUT, put); | |
398 | } | |
399 | ||
400 | static int | |
401 | dpif_linux_flow_del(struct dpif *dpif_, struct odp_flow *flow) | |
402 | { | |
403 | return do_ioctl(dpif_, ODP_FLOW_DEL, flow); | |
404 | } | |
405 | ||
406 | static int | |
407 | dpif_linux_flow_list(const struct dpif *dpif_, struct odp_flow flows[], int n) | |
408 | { | |
409 | struct odp_flowvec fv; | |
410 | int error; | |
411 | ||
412 | fv.flows = flows; | |
413 | fv.n_flows = n; | |
414 | error = do_ioctl(dpif_, ODP_FLOW_LIST, &fv); | |
415 | return error ? -error : fv.n_flows; | |
416 | } | |
417 | ||
418 | static int | |
419 | dpif_linux_execute(struct dpif *dpif_, uint16_t in_port, | |
420 | const union odp_action actions[], int n_actions, | |
421 | const struct ofpbuf *buf) | |
422 | { | |
423 | struct odp_execute execute; | |
424 | memset(&execute, 0, sizeof execute); | |
425 | execute.in_port = in_port; | |
426 | execute.actions = (union odp_action *) actions; | |
427 | execute.n_actions = n_actions; | |
428 | execute.data = buf->data; | |
429 | execute.length = buf->size; | |
430 | return do_ioctl(dpif_, ODP_EXECUTE, &execute); | |
431 | } | |
432 | ||
433 | static int | |
434 | dpif_linux_recv_get_mask(const struct dpif *dpif_, int *listen_mask) | |
435 | { | |
436 | return do_ioctl(dpif_, ODP_GET_LISTEN_MASK, listen_mask); | |
437 | } | |
438 | ||
439 | static int | |
440 | dpif_linux_recv_set_mask(struct dpif *dpif_, int listen_mask) | |
441 | { | |
442 | return do_ioctl(dpif_, ODP_SET_LISTEN_MASK, &listen_mask); | |
443 | } | |
444 | ||
72b06300 BP |
445 | static int |
446 | dpif_linux_get_sflow_probability(const struct dpif *dpif_, | |
447 | uint32_t *probability) | |
448 | { | |
449 | return do_ioctl(dpif_, ODP_GET_SFLOW_PROBABILITY, probability); | |
450 | } | |
451 | ||
452 | static int | |
453 | dpif_linux_set_sflow_probability(struct dpif *dpif_, uint32_t probability) | |
454 | { | |
455 | return do_ioctl(dpif_, ODP_SET_SFLOW_PROBABILITY, &probability); | |
456 | } | |
457 | ||
96fba48f BP |
458 | static int |
459 | dpif_linux_recv(struct dpif *dpif_, struct ofpbuf **bufp) | |
460 | { | |
461 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
462 | struct ofpbuf *buf; | |
463 | int retval; | |
464 | int error; | |
465 | ||
43253595 BP |
466 | buf = ofpbuf_new(65536 + DPIF_RECV_MSG_PADDING); |
467 | ofpbuf_reserve(buf, DPIF_RECV_MSG_PADDING); | |
96fba48f BP |
468 | retval = read(dpif->fd, ofpbuf_tail(buf), ofpbuf_tailroom(buf)); |
469 | if (retval < 0) { | |
470 | error = errno; | |
471 | if (error != EAGAIN) { | |
472 | VLOG_WARN_RL(&error_rl, "%s: read failed: %s", | |
473 | dpif_name(dpif_), strerror(error)); | |
474 | } | |
475 | } else if (retval >= sizeof(struct odp_msg)) { | |
476 | struct odp_msg *msg = buf->data; | |
477 | if (msg->length <= retval) { | |
478 | buf->size += retval; | |
479 | *bufp = buf; | |
480 | return 0; | |
481 | } else { | |
482 | VLOG_WARN_RL(&error_rl, "%s: discarding message truncated " | |
d65349ea | 483 | "from %"PRIu32" bytes to %d", |
96fba48f BP |
484 | dpif_name(dpif_), msg->length, retval); |
485 | error = ERANGE; | |
486 | } | |
487 | } else if (!retval) { | |
488 | VLOG_WARN_RL(&error_rl, "%s: unexpected end of file", dpif_name(dpif_)); | |
489 | error = EPROTO; | |
490 | } else { | |
491 | VLOG_WARN_RL(&error_rl, | |
492 | "%s: discarding too-short message (%d bytes)", | |
493 | dpif_name(dpif_), retval); | |
494 | error = ERANGE; | |
495 | } | |
496 | ||
497 | *bufp = NULL; | |
498 | ofpbuf_delete(buf); | |
499 | return error; | |
500 | } | |
501 | ||
502 | static void | |
503 | dpif_linux_recv_wait(struct dpif *dpif_) | |
504 | { | |
505 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
506 | poll_fd_wait(dpif->fd, POLLIN); | |
507 | } | |
508 | ||
509 | const struct dpif_class dpif_linux_class = { | |
1a6f1e2a | 510 | "system", |
8b61709d BP |
511 | NULL, |
512 | NULL, | |
d3d22744 | 513 | dpif_linux_enumerate, |
96fba48f BP |
514 | dpif_linux_open, |
515 | dpif_linux_close, | |
d3d22744 | 516 | dpif_linux_get_all_names, |
7dab847a | 517 | dpif_linux_destroy, |
96fba48f BP |
518 | dpif_linux_get_stats, |
519 | dpif_linux_get_drop_frags, | |
520 | dpif_linux_set_drop_frags, | |
521 | dpif_linux_port_add, | |
522 | dpif_linux_port_del, | |
523 | dpif_linux_port_query_by_number, | |
524 | dpif_linux_port_query_by_name, | |
525 | dpif_linux_port_list, | |
e9e28be3 BP |
526 | dpif_linux_port_poll, |
527 | dpif_linux_port_poll_wait, | |
96fba48f BP |
528 | dpif_linux_port_group_get, |
529 | dpif_linux_port_group_set, | |
530 | dpif_linux_flow_get, | |
531 | dpif_linux_flow_put, | |
532 | dpif_linux_flow_del, | |
533 | dpif_linux_flow_flush, | |
534 | dpif_linux_flow_list, | |
535 | dpif_linux_execute, | |
536 | dpif_linux_recv_get_mask, | |
537 | dpif_linux_recv_set_mask, | |
72b06300 BP |
538 | dpif_linux_get_sflow_probability, |
539 | dpif_linux_set_sflow_probability, | |
96fba48f BP |
540 | dpif_linux_recv, |
541 | dpif_linux_recv_wait, | |
542 | }; | |
543 | \f | |
544 | static int get_openvswitch_major(void); | |
57aaff8a | 545 | static int get_major(const char *target); |
96fba48f BP |
546 | |
547 | static int | |
548 | do_ioctl(const struct dpif *dpif_, int cmd, const void *arg) | |
549 | { | |
550 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
551 | return ioctl(dpif->fd, cmd, arg) ? errno : 0; | |
552 | } | |
553 | ||
554 | static int | |
a165b67e | 555 | lookup_minor(const char *name, int *minorp) |
96fba48f BP |
556 | { |
557 | struct ethtool_drvinfo drvinfo; | |
a165b67e | 558 | int minor, port_no; |
96fba48f BP |
559 | struct ifreq ifr; |
560 | int error; | |
561 | int sock; | |
562 | ||
563 | sock = socket(AF_INET, SOCK_DGRAM, 0); | |
564 | if (sock < 0) { | |
565 | VLOG_WARN("socket(AF_INET) failed: %s", strerror(errno)); | |
566 | error = errno; | |
567 | goto error; | |
568 | } | |
569 | ||
570 | memset(&ifr, 0, sizeof ifr); | |
571 | strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); | |
572 | ifr.ifr_data = (caddr_t) &drvinfo; | |
573 | ||
574 | memset(&drvinfo, 0, sizeof drvinfo); | |
575 | drvinfo.cmd = ETHTOOL_GDRVINFO; | |
576 | if (ioctl(sock, SIOCETHTOOL, &ifr)) { | |
577 | VLOG_WARN("ioctl(SIOCETHTOOL) failed: %s", strerror(errno)); | |
578 | error = errno; | |
579 | goto error_close_sock; | |
580 | } | |
581 | ||
582 | if (strcmp(drvinfo.driver, "openvswitch")) { | |
583 | VLOG_WARN("%s is not an openvswitch device", name); | |
584 | error = EOPNOTSUPP; | |
585 | goto error_close_sock; | |
586 | } | |
587 | ||
a165b67e BP |
588 | if (sscanf(drvinfo.bus_info, "%d.%d", &minor, &port_no) != 2) { |
589 | VLOG_WARN("%s ethtool bus_info has unexpected format", name); | |
96fba48f BP |
590 | error = EPROTOTYPE; |
591 | goto error_close_sock; | |
a165b67e BP |
592 | } else if (port_no != ODPP_LOCAL) { |
593 | /* This is an Open vSwitch device but not the local port. We | |
594 | * intentionally support only using the name of the local port as the | |
595 | * name of a datapath; otherwise, it would be too difficult to | |
596 | * enumerate all the names of a datapath. */ | |
597 | error = EOPNOTSUPP; | |
598 | goto error_close_sock; | |
96fba48f BP |
599 | } |
600 | ||
a165b67e | 601 | *minorp = minor; |
96fba48f BP |
602 | close(sock); |
603 | return 0; | |
604 | ||
605 | error_close_sock: | |
606 | close(sock); | |
607 | error: | |
608 | return error; | |
609 | } | |
610 | ||
611 | static int | |
612 | make_openvswitch_device(int minor, char **fnp) | |
613 | { | |
96fba48f | 614 | const char dirname[] = "/dev/net"; |
57aaff8a JP |
615 | int major; |
616 | dev_t dev; | |
96fba48f BP |
617 | struct stat s; |
618 | char fn[128]; | |
619 | ||
8334b477 BP |
620 | *fnp = NULL; |
621 | ||
57aaff8a JP |
622 | major = get_openvswitch_major(); |
623 | if (major < 0) { | |
624 | return -major; | |
625 | } | |
626 | dev = makedev(major, minor); | |
627 | ||
96fba48f BP |
628 | sprintf(fn, "%s/dp%d", dirname, minor); |
629 | if (!stat(fn, &s)) { | |
630 | if (!S_ISCHR(s.st_mode)) { | |
631 | VLOG_WARN_RL(&error_rl, "%s is not a character device, fixing", | |
632 | fn); | |
633 | } else if (s.st_rdev != dev) { | |
634 | VLOG_WARN_RL(&error_rl, | |
f17d7bd8 | 635 | "%s is device %u:%u but should be %u:%u, fixing", |
96fba48f BP |
636 | fn, major(s.st_rdev), minor(s.st_rdev), |
637 | major(dev), minor(dev)); | |
638 | } else { | |
639 | goto success; | |
640 | } | |
641 | if (unlink(fn)) { | |
642 | VLOG_WARN_RL(&error_rl, "%s: unlink failed (%s)", | |
643 | fn, strerror(errno)); | |
644 | return errno; | |
645 | } | |
646 | } else if (errno == ENOENT) { | |
647 | if (stat(dirname, &s)) { | |
648 | if (errno == ENOENT) { | |
649 | if (mkdir(dirname, 0755)) { | |
650 | VLOG_WARN_RL(&error_rl, "%s: mkdir failed (%s)", | |
651 | dirname, strerror(errno)); | |
652 | return errno; | |
653 | } | |
654 | } else { | |
655 | VLOG_WARN_RL(&error_rl, "%s: stat failed (%s)", | |
656 | dirname, strerror(errno)); | |
657 | return errno; | |
658 | } | |
659 | } | |
660 | } else { | |
661 | VLOG_WARN_RL(&error_rl, "%s: stat failed (%s)", fn, strerror(errno)); | |
662 | return errno; | |
663 | } | |
664 | ||
665 | /* The device needs to be created. */ | |
666 | if (mknod(fn, S_IFCHR | 0700, dev)) { | |
667 | VLOG_WARN_RL(&error_rl, | |
668 | "%s: creating character device %u:%u failed (%s)", | |
669 | fn, major(dev), minor(dev), strerror(errno)); | |
670 | return errno; | |
671 | } | |
672 | ||
673 | success: | |
674 | *fnp = xstrdup(fn); | |
675 | return 0; | |
676 | } | |
677 | ||
57aaff8a JP |
678 | /* Return the major device number of the Open vSwitch device. If it |
679 | * cannot be determined, a negative errno is returned. */ | |
96fba48f BP |
680 | static int |
681 | get_openvswitch_major(void) | |
682 | { | |
57aaff8a JP |
683 | static int openvswitch_major = -1; |
684 | if (openvswitch_major < 0) { | |
685 | openvswitch_major = get_major("openvswitch"); | |
96fba48f BP |
686 | } |
687 | return openvswitch_major; | |
688 | } | |
689 | ||
690 | static int | |
57aaff8a | 691 | get_major(const char *target) |
96fba48f BP |
692 | { |
693 | const char fn[] = "/proc/devices"; | |
694 | char line[128]; | |
695 | FILE *file; | |
696 | int ln; | |
697 | ||
698 | file = fopen(fn, "r"); | |
699 | if (!file) { | |
700 | VLOG_ERR("opening %s failed (%s)", fn, strerror(errno)); | |
57aaff8a | 701 | return -errno; |
96fba48f BP |
702 | } |
703 | ||
704 | for (ln = 1; fgets(line, sizeof line, file); ln++) { | |
705 | char name[64]; | |
706 | int major; | |
707 | ||
708 | if (!strncmp(line, "Character", 9) || line[0] == '\0') { | |
709 | /* Nothing to do. */ | |
710 | } else if (!strncmp(line, "Block", 5)) { | |
711 | /* We only want character devices, so skip the rest of the file. */ | |
712 | break; | |
713 | } else if (sscanf(line, "%d %63s", &major, name)) { | |
714 | if (!strcmp(name, target)) { | |
715 | fclose(file); | |
716 | return major; | |
717 | } | |
718 | } else { | |
719 | static bool warned; | |
720 | if (!warned) { | |
721 | VLOG_WARN("%s:%d: syntax error", fn, ln); | |
722 | } | |
723 | warned = true; | |
724 | } | |
725 | } | |
726 | ||
ed30fb10 TN |
727 | fclose(file); |
728 | ||
57aaff8a JP |
729 | VLOG_ERR("%s: %s major not found (is the module loaded?)", fn, target); |
730 | return -ENODEV; | |
96fba48f BP |
731 | } |
732 | ||
e9e28be3 BP |
733 | static int |
734 | finish_open(struct dpif *dpif_, const char *local_ifname) | |
735 | { | |
736 | struct dpif_linux *dpif = dpif_linux_cast(dpif_); | |
999401aa | 737 | dpif->local_ifname = xstrdup(local_ifname); |
e9e28be3 BP |
738 | dpif->local_ifindex = if_nametoindex(local_ifname); |
739 | if (!dpif->local_ifindex) { | |
740 | int error = errno; | |
999401aa | 741 | dpif_uninit(dpif_, true); |
e9e28be3 BP |
742 | VLOG_WARN("could not get ifindex of %s device: %s", |
743 | local_ifname, strerror(errno)); | |
744 | return error; | |
745 | } | |
746 | return 0; | |
747 | } | |
748 | ||
96fba48f BP |
749 | static int |
750 | create_minor(const char *name, int minor, struct dpif **dpifp) | |
751 | { | |
752 | int error = open_minor(minor, dpifp); | |
753 | if (!error) { | |
754 | error = do_ioctl(*dpifp, ODP_DP_CREATE, name); | |
e9e28be3 BP |
755 | if (!error) { |
756 | error = finish_open(*dpifp, name); | |
757 | } else { | |
999401aa | 758 | dpif_uninit(*dpifp, true); |
96fba48f BP |
759 | } |
760 | } | |
761 | return error; | |
762 | } | |
763 | ||
764 | static int | |
765 | open_minor(int minor, struct dpif **dpifp) | |
766 | { | |
767 | int error; | |
768 | char *fn; | |
769 | int fd; | |
770 | ||
771 | error = make_openvswitch_device(minor, &fn); | |
772 | if (error) { | |
773 | return error; | |
774 | } | |
775 | ||
776 | fd = open(fn, O_RDONLY | O_NONBLOCK); | |
777 | if (fd >= 0) { | |
e9e28be3 | 778 | struct dpif_linux *dpif = xmalloc(sizeof *dpif); |
46097491 BP |
779 | error = rtnetlink_notifier_register(&dpif->port_notifier, |
780 | dpif_linux_port_changed, dpif); | |
e9e28be3 BP |
781 | if (!error) { |
782 | char *name; | |
783 | ||
784 | name = xasprintf("dp%d", minor); | |
785 | dpif_init(&dpif->dpif, &dpif_linux_class, name, minor, minor); | |
786 | free(name); | |
787 | ||
788 | dpif->fd = fd; | |
d3d22744 BP |
789 | dpif->local_ifname = NULL; |
790 | dpif->minor = minor; | |
e9e28be3 | 791 | dpif->local_ifindex = 0; |
54825e09 | 792 | shash_init(&dpif->changed_ports); |
8b61709d | 793 | dpif->change_error = false; |
e9e28be3 BP |
794 | *dpifp = &dpif->dpif; |
795 | } else { | |
796 | free(dpif); | |
797 | } | |
96fba48f BP |
798 | } else { |
799 | error = errno; | |
800 | VLOG_WARN("%s: open failed (%s)", fn, strerror(error)); | |
801 | } | |
802 | free(fn); | |
803 | ||
804 | return error; | |
805 | } | |
e9e28be3 BP |
806 | |
807 | static void | |
46097491 | 808 | dpif_linux_port_changed(const struct rtnetlink_change *change, void *dpif_) |
e9e28be3 BP |
809 | { |
810 | struct dpif_linux *dpif = dpif_; | |
811 | ||
8b61709d BP |
812 | if (change) { |
813 | if (change->master_ifindex == dpif->local_ifindex | |
814 | && (change->nlmsg_type == RTM_NEWLINK | |
815 | || change->nlmsg_type == RTM_DELLINK)) | |
816 | { | |
817 | /* Our datapath changed, either adding a new port or deleting an | |
818 | * existing one. */ | |
54825e09 | 819 | shash_add_once(&dpif->changed_ports, change->ifname, NULL); |
e9e28be3 | 820 | } |
8b61709d BP |
821 | } else { |
822 | dpif->change_error = true; | |
e9e28be3 BP |
823 | } |
824 | } |