]>
Commit | Line | Data |
---|---|---|
e0edde6f | 1 | /* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. |
064af421 | 2 | * |
a14bc59f BP |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
064af421 | 6 | * |
a14bc59f | 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
064af421 | 8 | * |
a14bc59f BP |
9 | * Unless required by applicable law or agreed to in writing, software |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
064af421 BP |
14 | */ |
15 | ||
16 | #include <config.h> | |
17 | ||
3c303e5f | 18 | #include <asm/param.h> |
064af421 BP |
19 | #include <assert.h> |
20 | #include <errno.h> | |
21 | #include <getopt.h> | |
22 | #include <inttypes.h> | |
23 | #include <limits.h> | |
24 | #include <net/if.h> | |
25 | #include <linux/genetlink.h> | |
26 | #include <linux/rtnetlink.h> | |
27 | #include <signal.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <sys/types.h> | |
5b44dc99 | 31 | #include <sys/wait.h> |
064af421 | 32 | #include <sys/stat.h> |
3c303e5f | 33 | #include <time.h> |
064af421 BP |
34 | #include <fcntl.h> |
35 | #include <unistd.h> | |
36 | ||
064af421 BP |
37 | #include "command-line.h" |
38 | #include "coverage.h" | |
39 | #include "daemon.h" | |
40 | #include "dirs.h" | |
3c303e5f | 41 | #include "dynamic-string.h" |
064af421 | 42 | #include "fatal-signal.h" |
1e86ae6f | 43 | #include "json.h" |
064af421 BP |
44 | #include "leak-checker.h" |
45 | #include "netdev.h" | |
46 | #include "netlink.h" | |
45c8d3a1 | 47 | #include "netlink-notifier.h" |
2fe27d5a | 48 | #include "netlink-socket.h" |
064af421 BP |
49 | #include "ofpbuf.h" |
50 | #include "openvswitch/brcompat-netlink.h" | |
3c303e5f | 51 | #include "packets.h" |
064af421 BP |
52 | #include "poll-loop.h" |
53 | #include "process.h" | |
5326a0fd | 54 | #include "rtnetlink-link.h" |
064af421 | 55 | #include "signals.h" |
96ca8c29 | 56 | #include "sset.h" |
2b01925c | 57 | #include "svec.h" |
064af421 BP |
58 | #include "timeval.h" |
59 | #include "unixctl.h" | |
60 | #include "util.h" | |
5136ce49 | 61 | #include "vlog.h" |
064af421 | 62 | |
d98e6007 | 63 | VLOG_DEFINE_THIS_MODULE(brcompatd); |
064af421 | 64 | |
064af421 BP |
65 | /* xxx Just hangs if datapath is rmmod/insmod. Learn to reconnect? */ |
66 | ||
2b01925c | 67 | static void parse_options(int argc, char *argv[]); |
064af421 BP |
68 | static void usage(void) NO_RETURN; |
69 | ||
70 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60); | |
71 | ||
2b01925c BP |
72 | /* --appctl: Absolute path to ovs-appctl. */ |
73 | static char *appctl_program; | |
74 | ||
75 | /* --vsctl: Absolute path to ovs-vsctl. */ | |
76 | static char *vsctl_program; | |
77 | ||
78 | /* Options that we should generally pass to ovs-vsctl. */ | |
ea523221 | 79 | #define VSCTL_OPTIONS "--timeout=5", "-vconsole:warn" |
064af421 | 80 | |
064af421 BP |
81 | /* Netlink socket to bridge compatibility kernel module. */ |
82 | static struct nl_sock *brc_sock; | |
83 | ||
84 | /* The Generic Netlink family number used for bridge compatibility. */ | |
85 | static int brc_family; | |
86 | ||
87 | static const struct nl_policy brc_multicast_policy[] = { | |
88 | [BRC_GENL_A_MC_GROUP] = {.type = NL_A_U32 } | |
89 | }; | |
90 | ||
2b01925c BP |
91 | static char * |
92 | capture_vsctl_valist(const char *arg0, va_list args) | |
93 | { | |
94 | char *stdout_log, *stderr_log; | |
95 | enum vlog_level log_level; | |
96 | struct svec argv; | |
97 | int status; | |
98 | char *msg; | |
99 | ||
100 | /* Compose arguments. */ | |
101 | svec_init(&argv); | |
102 | svec_add(&argv, arg0); | |
103 | for (;;) { | |
104 | const char *arg = va_arg(args, const char *); | |
105 | if (!arg) { | |
106 | break; | |
107 | } | |
108 | svec_add(&argv, arg); | |
109 | } | |
110 | svec_terminate(&argv); | |
111 | ||
112 | /* Run process. */ | |
113 | if (process_run_capture(argv.names, &stdout_log, &stderr_log, SIZE_MAX, | |
114 | &status)) { | |
115 | svec_destroy(&argv); | |
116 | return NULL; | |
117 | } | |
118 | ||
119 | /* Log results. */ | |
120 | if (WIFEXITED(status)) { | |
121 | int code = WEXITSTATUS(status); | |
122 | log_level = code == 0 ? VLL_DBG : code == 1 ? VLL_WARN : VLL_ERR; | |
123 | } else { | |
124 | log_level = VLL_ERR; | |
125 | } | |
126 | msg = process_status_msg(status); | |
127 | VLOG(log_level, "ovs-vsctl exited (%s)", msg); | |
128 | if (stdout_log && *stdout_log) { | |
129 | VLOG(log_level, "ovs-vsctl wrote to stdout:\n%s\n", stdout_log); | |
130 | } | |
131 | if (stderr_log && *stderr_log) { | |
132 | VLOG(log_level, "ovs-vsctl wrote to stderr:\n%s\n", stderr_log); | |
133 | } | |
134 | free(msg); | |
135 | ||
136 | svec_destroy(&argv); | |
137 | ||
138 | free(stderr_log); | |
139 | if (WIFEXITED(status) && !WEXITSTATUS(status)) { | |
140 | return stdout_log; | |
141 | } else { | |
142 | free(stdout_log); | |
143 | return NULL; | |
144 | } | |
145 | } | |
146 | ||
147 | static char * SENTINEL(0) | |
148 | capture_vsctl(const char *arg0, ...) | |
149 | { | |
150 | char *stdout_log; | |
151 | va_list args; | |
152 | ||
153 | va_start(args, arg0); | |
154 | stdout_log = capture_vsctl_valist(arg0, args); | |
155 | va_end(args); | |
156 | ||
157 | return stdout_log; | |
158 | } | |
159 | ||
160 | static bool SENTINEL(0) | |
161 | run_vsctl(const char *arg0, ...) | |
162 | { | |
163 | char *stdout_log; | |
164 | va_list args; | |
165 | bool ok; | |
166 | ||
167 | va_start(args, arg0); | |
168 | stdout_log = capture_vsctl_valist(arg0, args); | |
169 | va_end(args); | |
170 | ||
171 | ok = stdout_log != NULL; | |
172 | free(stdout_log); | |
173 | return ok; | |
174 | } | |
175 | ||
064af421 BP |
176 | static int |
177 | lookup_brc_multicast_group(int *multicast_group) | |
178 | { | |
179 | struct nl_sock *sock; | |
180 | struct ofpbuf request, *reply; | |
181 | struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)]; | |
182 | int retval; | |
183 | ||
cceb11f5 | 184 | retval = nl_sock_create(NETLINK_GENERIC, &sock); |
064af421 BP |
185 | if (retval) { |
186 | return retval; | |
187 | } | |
188 | ofpbuf_init(&request, 0); | |
69123704 | 189 | nl_msg_put_genlmsghdr(&request, 0, brc_family, |
064af421 BP |
190 | NLM_F_REQUEST, BRC_GENL_C_QUERY_MC, 1); |
191 | retval = nl_sock_transact(sock, &request, &reply); | |
192 | ofpbuf_uninit(&request); | |
193 | if (retval) { | |
194 | nl_sock_destroy(sock); | |
195 | return retval; | |
196 | } | |
197 | if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN, | |
198 | brc_multicast_policy, attrs, | |
199 | ARRAY_SIZE(brc_multicast_policy))) { | |
200 | nl_sock_destroy(sock); | |
201 | ofpbuf_delete(reply); | |
202 | return EPROTO; | |
203 | } | |
204 | *multicast_group = nl_attr_get_u32(attrs[BRC_GENL_A_MC_GROUP]); | |
205 | nl_sock_destroy(sock); | |
206 | ofpbuf_delete(reply); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | /* Opens a socket for brcompat notifications. Returns 0 if successful, | |
212 | * otherwise a positive errno value. */ | |
213 | static int | |
214 | brc_open(struct nl_sock **sock) | |
215 | { | |
216 | int multicast_group = 0; | |
217 | int retval; | |
218 | ||
219 | retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family); | |
220 | if (retval) { | |
221 | return retval; | |
222 | } | |
223 | ||
224 | retval = lookup_brc_multicast_group(&multicast_group); | |
225 | if (retval) { | |
226 | return retval; | |
227 | } | |
228 | ||
cceb11f5 | 229 | retval = nl_sock_create(NETLINK_GENERIC, sock); |
064af421 BP |
230 | if (retval) { |
231 | return retval; | |
232 | } | |
233 | ||
cceb11f5 BP |
234 | retval = nl_sock_join_mcgroup(*sock, multicast_group); |
235 | if (retval) { | |
236 | nl_sock_destroy(*sock); | |
237 | *sock = NULL; | |
238 | } | |
239 | return retval; | |
064af421 BP |
240 | } |
241 | ||
242 | static const struct nl_policy brc_dp_policy[] = { | |
243 | [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING }, | |
244 | }; | |
245 | ||
064af421 BP |
246 | static int |
247 | parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name, | |
3c303e5f | 248 | const char **port_name, uint64_t *count, uint64_t *skip) |
064af421 BP |
249 | { |
250 | static const struct nl_policy policy[] = { | |
7f42c1d7 | 251 | [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING, .optional = true }, |
064af421 | 252 | [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true }, |
3c303e5f BP |
253 | [BRC_GENL_A_FDB_COUNT] = { .type = NL_A_U64, .optional = true }, |
254 | [BRC_GENL_A_FDB_SKIP] = { .type = NL_A_U64, .optional = true }, | |
064af421 BP |
255 | }; |
256 | struct nlattr *attrs[ARRAY_SIZE(policy)]; | |
257 | ||
258 | if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy, | |
259 | attrs, ARRAY_SIZE(policy)) | |
7f42c1d7 | 260 | || (br_name && !attrs[BRC_GENL_A_DP_NAME]) |
3c303e5f BP |
261 | || (port_name && !attrs[BRC_GENL_A_PORT_NAME]) |
262 | || (count && !attrs[BRC_GENL_A_FDB_COUNT]) | |
263 | || (skip && !attrs[BRC_GENL_A_FDB_SKIP])) { | |
064af421 BP |
264 | return EINVAL; |
265 | } | |
266 | ||
267 | *seq = ((struct nlmsghdr *) buffer->data)->nlmsg_seq; | |
7f42c1d7 BP |
268 | if (br_name) { |
269 | *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]); | |
270 | } | |
064af421 BP |
271 | if (port_name) { |
272 | *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]); | |
273 | } | |
3c303e5f BP |
274 | if (count) { |
275 | *count = nl_attr_get_u64(attrs[BRC_GENL_A_FDB_COUNT]); | |
276 | } | |
277 | if (skip) { | |
278 | *skip = nl_attr_get_u64(attrs[BRC_GENL_A_FDB_SKIP]); | |
279 | } | |
064af421 BP |
280 | return 0; |
281 | } | |
282 | ||
ff459dd6 BP |
283 | /* Composes and returns a reply to a request made by the datapath with error |
284 | * code 'error'. The caller may add additional attributes to the message, then | |
285 | * it may send it with send_reply(). */ | |
41e754bc | 286 | static struct ofpbuf * |
ff459dd6 | 287 | compose_reply(int error) |
064af421 | 288 | { |
41e754bc | 289 | struct ofpbuf *reply = ofpbuf_new(4096); |
69123704 | 290 | nl_msg_put_genlmsghdr(reply, 32, brc_family, NLM_F_REQUEST, |
064af421 | 291 | BRC_GENL_C_DP_RESULT, 1); |
41e754bc BP |
292 | nl_msg_put_u32(reply, BRC_GENL_A_ERR_CODE, error); |
293 | return reply; | |
294 | } | |
064af421 | 295 | |
ff459dd6 BP |
296 | /* Sends 'reply' to the datapath, using sequence number 'nlmsg_seq', and frees |
297 | * it. */ | |
41e754bc | 298 | static void |
ff459dd6 | 299 | send_reply(struct ofpbuf *reply, uint32_t nlmsg_seq) |
41e754bc | 300 | { |
ff459dd6 | 301 | int retval = nl_sock_send_seq(brc_sock, reply, nlmsg_seq, false); |
064af421 BP |
302 | if (retval) { |
303 | VLOG_WARN_RL(&rl, "replying to brcompat request: %s", | |
304 | strerror(retval)); | |
305 | } | |
41e754bc BP |
306 | ofpbuf_delete(reply); |
307 | } | |
308 | ||
309 | /* Composes and sends a reply to a request made by the datapath with Netlink | |
310 | * sequence number 'seq' and error code 'error'. */ | |
311 | static void | |
312 | send_simple_reply(uint32_t seq, int error) | |
313 | { | |
ff459dd6 | 314 | send_reply(compose_reply(error), seq); |
064af421 BP |
315 | } |
316 | ||
317 | static int | |
2b01925c | 318 | handle_bridge_cmd(struct ofpbuf *buffer, bool add) |
064af421 BP |
319 | { |
320 | const char *br_name; | |
321 | uint32_t seq; | |
322 | int error; | |
323 | ||
3c303e5f | 324 | error = parse_command(buffer, &seq, &br_name, NULL, NULL, NULL); |
064af421 | 325 | if (!error) { |
2b01925c BP |
326 | const char *vsctl_cmd = add ? "add-br" : "del-br"; |
327 | const char *brctl_cmd = add ? "addbr" : "delbr"; | |
328 | if (!run_vsctl(vsctl_program, VSCTL_OPTIONS, | |
329 | "--", vsctl_cmd, br_name, | |
330 | "--", "comment", "ovs-brcompatd:", brctl_cmd, br_name, | |
331 | (char *) NULL)) { | |
332 | error = add ? EEXIST : ENXIO; | |
333 | } | |
41e754bc | 334 | send_simple_reply(seq, error); |
064af421 BP |
335 | } |
336 | return error; | |
337 | } | |
338 | ||
339 | static const struct nl_policy brc_port_policy[] = { | |
340 | [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING }, | |
341 | [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING }, | |
342 | }; | |
343 | ||
064af421 | 344 | static int |
2b01925c | 345 | handle_port_cmd(struct ofpbuf *buffer, bool add) |
064af421 | 346 | { |
064af421 BP |
347 | const char *br_name, *port_name; |
348 | uint32_t seq; | |
349 | int error; | |
350 | ||
3c303e5f | 351 | error = parse_command(buffer, &seq, &br_name, &port_name, NULL, NULL); |
064af421 | 352 | if (!error) { |
2b01925c BP |
353 | const char *vsctl_cmd = add ? "add-port" : "del-port"; |
354 | const char *brctl_cmd = add ? "addif" : "delif"; | |
355 | if (!run_vsctl(vsctl_program, VSCTL_OPTIONS, | |
356 | "--", vsctl_cmd, br_name, port_name, | |
357 | "--", "comment", "ovs-brcompatd:", brctl_cmd, | |
358 | br_name, port_name, (char *) NULL)) { | |
064af421 | 359 | error = EINVAL; |
064af421 | 360 | } |
41e754bc | 361 | send_simple_reply(seq, error); |
064af421 | 362 | } |
064af421 BP |
363 | return error; |
364 | } | |
365 | ||
2b01925c | 366 | static char * |
9cb8877c | 367 | linux_bridge_to_ovs_bridge(const char *linux_name, int *br_vlanp) |
ae1281cf | 368 | { |
2b01925c | 369 | char *save_ptr = NULL; |
9cb8877c | 370 | const char *br_name, *br_vlan; |
2b01925c BP |
371 | char *br_name_copy; |
372 | char *output; | |
ae1281cf | 373 | |
9cb8877c BP |
374 | output = capture_vsctl(vsctl_program, VSCTL_OPTIONS, |
375 | "--", "br-to-parent", linux_name, | |
376 | "--", "br-to-vlan", linux_name, | |
377 | (char *) NULL); | |
2b01925c BP |
378 | if (!output) { |
379 | return NULL; | |
380 | } | |
381 | ||
382 | br_name = strtok_r(output, " \t\r\n", &save_ptr); | |
9cb8877c BP |
383 | br_vlan = strtok_r(NULL, " \t\r\n", &save_ptr); |
384 | if (!br_name || !br_vlan) { | |
2b01925c BP |
385 | free(output); |
386 | return NULL; | |
387 | } | |
388 | br_name_copy = xstrdup(br_name); | |
9cb8877c | 389 | *br_vlanp = atoi(br_vlan); |
2b01925c BP |
390 | |
391 | free(output); | |
392 | ||
393 | return br_name_copy; | |
394 | } | |
395 | ||
396 | static void | |
397 | get_bridge_ifaces(const char *br_name, struct sset *ifaces) | |
398 | { | |
399 | char *save_ptr = NULL; | |
400 | char *output; | |
401 | char *iface; | |
402 | ||
403 | output = capture_vsctl(vsctl_program, VSCTL_OPTIONS, "list-ifaces", | |
404 | br_name, (char *) NULL); | |
405 | if (!output) { | |
406 | return; | |
407 | } | |
408 | ||
409 | for (iface = strtok_r(output, " \t\r\n", &save_ptr); iface; | |
410 | iface = strtok_r(NULL, " \t\r\n", &save_ptr)) { | |
411 | sset_add(ifaces, iface); | |
ae1281cf | 412 | } |
2b01925c | 413 | free(output); |
ae1281cf BP |
414 | } |
415 | ||
3c303e5f | 416 | static int |
2b01925c | 417 | handle_fdb_query_cmd(struct ofpbuf *buffer) |
3c303e5f BP |
418 | { |
419 | /* This structure is copied directly from the Linux 2.6.30 header files. | |
420 | * It would be more straightforward to #include <linux/if_bridge.h>, but | |
421 | * the 'port_hi' member was only introduced in Linux 2.6.26 and so systems | |
422 | * with old header files won't have it. */ | |
423 | struct __fdb_entry { | |
424 | __u8 mac_addr[6]; | |
425 | __u8 port_no; | |
426 | __u8 is_local; | |
427 | __u32 ageing_timer_value; | |
428 | __u8 port_hi; | |
429 | __u8 pad0; | |
430 | __u16 unused; | |
431 | }; | |
432 | ||
433 | struct mac { | |
434 | uint8_t addr[6]; | |
435 | }; | |
436 | struct mac *local_macs; | |
437 | int n_local_macs; | |
438 | int i; | |
439 | ||
c735214e BP |
440 | /* Impedance matching between the vswitchd and Linux kernel notions of what |
441 | * a bridge is. The kernel only handles a single VLAN per bridge, but | |
442 | * vswitchd can deal with all the VLANs on a single bridge. We have to | |
443 | * pretend that the former is the case even though the latter is the | |
444 | * implementation. */ | |
9852694f | 445 | const char *linux_name; /* Name used by brctl. */ |
c735214e | 446 | int br_vlan; /* VLAN tag. */ |
96ca8c29 | 447 | struct sset ifaces; |
c735214e | 448 | |
3c303e5f | 449 | struct ofpbuf query_data; |
96ca8c29 | 450 | const char *iface_name; |
41e754bc | 451 | struct ofpbuf *reply; |
3c303e5f | 452 | uint64_t count, skip; |
2b01925c | 453 | char *br_name; |
3c303e5f BP |
454 | char *output; |
455 | char *save_ptr; | |
456 | uint32_t seq; | |
457 | int error; | |
458 | ||
9b80f761 | 459 | /* Parse the command received from brcompat. */ |
9852694f | 460 | error = parse_command(buffer, &seq, &linux_name, NULL, &count, &skip); |
3c303e5f BP |
461 | if (error) { |
462 | return error; | |
463 | } | |
464 | ||
c735214e | 465 | /* Figure out vswitchd bridge and VLAN. */ |
9cb8877c | 466 | br_name = linux_bridge_to_ovs_bridge(linux_name, &br_vlan); |
2b01925c BP |
467 | if (!br_name) { |
468 | error = EINVAL; | |
ae1281cf BP |
469 | send_simple_reply(seq, error); |
470 | return error; | |
c735214e BP |
471 | } |
472 | ||
3c303e5f | 473 | /* Fetch the forwarding database using ovs-appctl. */ |
2b01925c BP |
474 | output = capture_vsctl(appctl_program, "fdb/show", br_name, |
475 | (char *) NULL); | |
476 | if (!output) { | |
477 | error = ECHILD; | |
41e754bc | 478 | send_simple_reply(seq, error); |
3c303e5f BP |
479 | return error; |
480 | } | |
481 | ||
482 | /* Fetch the MAC address for each interface on the bridge, so that we can | |
483 | * fill in the is_local field in the response. */ | |
96ca8c29 | 484 | sset_init(&ifaces); |
2b01925c | 485 | get_bridge_ifaces(linux_name, &ifaces); |
96ca8c29 | 486 | local_macs = xmalloc(sset_count(&ifaces) * sizeof *local_macs); |
3c303e5f | 487 | n_local_macs = 0; |
96ca8c29 | 488 | SSET_FOR_EACH (iface_name, &ifaces) { |
3c303e5f | 489 | struct mac *mac = &local_macs[n_local_macs]; |
07c318f4 BP |
490 | struct netdev *netdev; |
491 | ||
18812dff | 492 | error = netdev_open(iface_name, "system", &netdev); |
4869f1b1 | 493 | if (!error) { |
07c318f4 BP |
494 | if (!netdev_get_etheraddr(netdev, mac->addr)) { |
495 | n_local_macs++; | |
496 | } | |
497 | netdev_close(netdev); | |
3c303e5f BP |
498 | } |
499 | } | |
96ca8c29 | 500 | sset_destroy(&ifaces); |
3c303e5f BP |
501 | |
502 | /* Parse the response from ovs-appctl and convert it to binary format to | |
503 | * pass back to the kernel. */ | |
504 | ofpbuf_init(&query_data, sizeof(struct __fdb_entry) * 8); | |
505 | save_ptr = NULL; | |
506 | strtok_r(output, "\n", &save_ptr); /* Skip header line. */ | |
507 | while (count > 0) { | |
508 | struct __fdb_entry *entry; | |
509 | int port, vlan, age; | |
510 | uint8_t mac[ETH_ADDR_LEN]; | |
511 | char *line; | |
512 | bool is_local; | |
513 | ||
514 | line = strtok_r(NULL, "\n", &save_ptr); | |
515 | if (!line) { | |
516 | break; | |
517 | } | |
518 | ||
519 | if (sscanf(line, "%d %d "ETH_ADDR_SCAN_FMT" %d", | |
520 | &port, &vlan, ETH_ADDR_SCAN_ARGS(mac), &age) | |
521 | != 2 + ETH_ADDR_SCAN_COUNT + 1) { | |
db5ce514 | 522 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); |
3c303e5f BP |
523 | VLOG_INFO_RL(&rl, "fdb/show output has invalid format: %s", line); |
524 | continue; | |
525 | } | |
526 | ||
c735214e BP |
527 | if (vlan != br_vlan) { |
528 | continue; | |
529 | } | |
530 | ||
3c303e5f BP |
531 | if (skip > 0) { |
532 | skip--; | |
533 | continue; | |
534 | } | |
535 | ||
536 | /* Is this the MAC address of an interface on the bridge? */ | |
537 | is_local = false; | |
538 | for (i = 0; i < n_local_macs; i++) { | |
539 | if (eth_addr_equals(local_macs[i].addr, mac)) { | |
540 | is_local = true; | |
541 | break; | |
542 | } | |
543 | } | |
544 | ||
545 | entry = ofpbuf_put_uninit(&query_data, sizeof *entry); | |
546 | memcpy(entry->mac_addr, mac, ETH_ADDR_LEN); | |
547 | entry->port_no = port & 0xff; | |
548 | entry->is_local = is_local; | |
549 | entry->ageing_timer_value = age * HZ; | |
550 | entry->port_hi = (port & 0xff00) >> 8; | |
551 | entry->pad0 = 0; | |
552 | entry->unused = 0; | |
553 | count--; | |
554 | } | |
555 | free(output); | |
556 | ||
41e754bc | 557 | /* Compose and send reply to datapath. */ |
ff459dd6 | 558 | reply = compose_reply(0); |
41e754bc BP |
559 | nl_msg_put_unspec(reply, BRC_GENL_A_FDB_DATA, |
560 | query_data.data, query_data.size); | |
ff459dd6 | 561 | send_reply(reply, seq); |
41e754bc BP |
562 | |
563 | /* Free memory. */ | |
3c303e5f | 564 | ofpbuf_uninit(&query_data); |
05edc34c | 565 | free(local_macs); |
3c303e5f BP |
566 | |
567 | return 0; | |
568 | } | |
569 | ||
db322751 | 570 | static void |
2b01925c | 571 | send_ifindex_reply(uint32_t seq, char *output) |
db322751 | 572 | { |
2b01925c BP |
573 | size_t allocated_indices; |
574 | char *save_ptr = NULL; | |
db322751 BP |
575 | struct ofpbuf *reply; |
576 | const char *iface; | |
577 | size_t n_indices; | |
578 | int *indices; | |
db322751 | 579 | |
2b01925c BP |
580 | indices = NULL; |
581 | n_indices = allocated_indices = 0; | |
582 | for (iface = strtok_r(output, " \t\r\n", &save_ptr); iface; | |
583 | iface = strtok_r(NULL, " \t\r\n", &save_ptr)) { | |
584 | int ifindex; | |
585 | ||
586 | if (n_indices >= allocated_indices) { | |
587 | indices = x2nrealloc(indices, &allocated_indices, sizeof *indices); | |
588 | } | |
589 | ||
590 | ifindex = if_nametoindex(iface); | |
db322751 BP |
591 | if (ifindex) { |
592 | indices[n_indices++] = ifindex; | |
593 | } | |
594 | } | |
595 | ||
596 | /* Compose and send reply. */ | |
ff459dd6 | 597 | reply = compose_reply(0); |
db322751 BP |
598 | nl_msg_put_unspec(reply, BRC_GENL_A_IFINDEXES, |
599 | indices, n_indices * sizeof *indices); | |
ff459dd6 | 600 | send_reply(reply, seq); |
db322751 BP |
601 | |
602 | /* Free memory. */ | |
603 | free(indices); | |
604 | } | |
605 | ||
606 | static int | |
2b01925c | 607 | handle_get_bridges_cmd(struct ofpbuf *buffer) |
db322751 | 608 | { |
2b01925c | 609 | char *output; |
db322751 | 610 | uint32_t seq; |
db322751 BP |
611 | int error; |
612 | ||
613 | /* Parse Netlink command. | |
614 | * | |
615 | * The command doesn't actually have any arguments, but we need the | |
616 | * sequence number to send the reply. */ | |
617 | error = parse_command(buffer, &seq, NULL, NULL, NULL, NULL); | |
618 | if (error) { | |
619 | return error; | |
620 | } | |
621 | ||
2b01925c BP |
622 | output = capture_vsctl(vsctl_program, VSCTL_OPTIONS, "list-br", (char *) NULL); |
623 | if (!output) { | |
624 | return ENODEV; | |
db322751 BP |
625 | } |
626 | ||
2b01925c BP |
627 | send_ifindex_reply(seq, output); |
628 | free(output); | |
db322751 BP |
629 | return 0; |
630 | } | |
631 | ||
632 | static int | |
2b01925c | 633 | handle_get_ports_cmd(struct ofpbuf *buffer) |
db322751 | 634 | { |
9852694f | 635 | const char *linux_name; |
2b01925c BP |
636 | uint32_t seq; |
637 | char *output; | |
db322751 BP |
638 | int error; |
639 | ||
640 | /* Parse Netlink command. */ | |
9852694f | 641 | error = parse_command(buffer, &seq, &linux_name, NULL, NULL, NULL); |
db322751 BP |
642 | if (error) { |
643 | return error; | |
644 | } | |
645 | ||
2b01925c BP |
646 | output = capture_vsctl(vsctl_program, VSCTL_OPTIONS, "list-ports", linux_name, |
647 | (char *) NULL); | |
648 | if (!output) { | |
649 | return ENODEV; | |
db322751 BP |
650 | } |
651 | ||
2b01925c BP |
652 | send_ifindex_reply(seq, output); |
653 | free(output); | |
db322751 BP |
654 | return 0; |
655 | } | |
656 | ||
72d32ac0 BP |
657 | static bool |
658 | brc_recv_update__(struct ofpbuf *buffer) | |
1cec7ca1 BP |
659 | { |
660 | for (;;) { | |
72d32ac0 | 661 | int retval = nl_sock_recv(brc_sock, buffer, false); |
1cec7ca1 BP |
662 | switch (retval) { |
663 | case 0: | |
664 | if (nl_msg_nlmsgerr(buffer, NULL) | |
665 | || nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE) { | |
666 | break; | |
667 | } | |
72d32ac0 | 668 | return true; |
1cec7ca1 BP |
669 | |
670 | case ENOBUFS: | |
671 | break; | |
672 | ||
673 | case EAGAIN: | |
72d32ac0 | 674 | return false; |
1cec7ca1 BP |
675 | |
676 | default: | |
677 | VLOG_WARN_RL(&rl, "brc_recv_update: %s", strerror(retval)); | |
72d32ac0 | 678 | return false; |
1cec7ca1 | 679 | } |
1cec7ca1 BP |
680 | } |
681 | } | |
682 | ||
9852694f | 683 | static void |
2b01925c | 684 | brc_recv_update(void) |
064af421 | 685 | { |
064af421 | 686 | struct genlmsghdr *genlmsghdr; |
72d32ac0 BP |
687 | uint64_t buffer_stub[1024 / 8]; |
688 | struct ofpbuf buffer; | |
064af421 | 689 | |
72d32ac0 BP |
690 | ofpbuf_use_stub(&buffer, buffer_stub, sizeof buffer_stub); |
691 | if (!brc_recv_update__(&buffer)) { | |
692 | goto error; | |
064af421 BP |
693 | } |
694 | ||
72d32ac0 | 695 | genlmsghdr = nl_msg_genlmsghdr(&buffer); |
064af421 BP |
696 | if (!genlmsghdr) { |
697 | VLOG_WARN_RL(&rl, "received packet too short for generic NetLink"); | |
698 | goto error; | |
699 | } | |
700 | ||
72d32ac0 | 701 | if (nl_msg_nlmsghdr(&buffer)->nlmsg_type != brc_family) { |
064af421 | 702 | VLOG_DBG_RL(&rl, "received type (%"PRIu16") != brcompat family (%d)", |
72d32ac0 | 703 | nl_msg_nlmsghdr(&buffer)->nlmsg_type, brc_family); |
064af421 BP |
704 | goto error; |
705 | } | |
706 | ||
e1f406a3 BP |
707 | /* Service all pending network device notifications before executing the |
708 | * command. This is very important to avoid a race in a scenario like the | |
709 | * following, which is what happens with XenServer Tools version 5.0.0 | |
710 | * during boot of a Windows VM: | |
711 | * | |
712 | * 1. Create tap1.0 and vif1.0. | |
713 | * 2. Delete tap1.0. | |
714 | * 3. Delete vif1.0. | |
715 | * 4. Re-create vif1.0. | |
716 | * | |
717 | * We must process the network device notification from step 3 before we | |
718 | * process the brctl command from step 4. If we process them in the | |
719 | * reverse order, then step 4 completes as a no-op but step 3 then deletes | |
720 | * the port that was just added. | |
721 | * | |
722 | * (XenServer Tools 5.5.0 does not exhibit this behavior, and neither does | |
723 | * a VM without Tools installed at all.) | |
724 | */ | |
18a23781 | 725 | rtnetlink_link_run(); |
e1f406a3 | 726 | |
064af421 BP |
727 | switch (genlmsghdr->cmd) { |
728 | case BRC_GENL_C_DP_ADD: | |
72d32ac0 | 729 | handle_bridge_cmd(&buffer, true); |
064af421 BP |
730 | break; |
731 | ||
732 | case BRC_GENL_C_DP_DEL: | |
72d32ac0 | 733 | handle_bridge_cmd(&buffer, false); |
064af421 BP |
734 | break; |
735 | ||
736 | case BRC_GENL_C_PORT_ADD: | |
72d32ac0 | 737 | handle_port_cmd(&buffer, true); |
064af421 BP |
738 | break; |
739 | ||
740 | case BRC_GENL_C_PORT_DEL: | |
72d32ac0 | 741 | handle_port_cmd(&buffer, false); |
064af421 BP |
742 | break; |
743 | ||
3c303e5f | 744 | case BRC_GENL_C_FDB_QUERY: |
72d32ac0 | 745 | handle_fdb_query_cmd(&buffer); |
3c303e5f BP |
746 | break; |
747 | ||
db322751 | 748 | case BRC_GENL_C_GET_BRIDGES: |
72d32ac0 | 749 | handle_get_bridges_cmd(&buffer); |
db322751 BP |
750 | break; |
751 | ||
752 | case BRC_GENL_C_GET_PORTS: | |
72d32ac0 | 753 | handle_get_ports_cmd(&buffer); |
db322751 BP |
754 | break; |
755 | ||
064af421 | 756 | default: |
9852694f | 757 | VLOG_WARN_RL(&rl, "received unknown brc netlink command: %d\n", |
1e86ae6f | 758 | genlmsghdr->cmd); |
9852694f | 759 | break; |
064af421 BP |
760 | } |
761 | ||
064af421 | 762 | error: |
72d32ac0 | 763 | ofpbuf_uninit(&buffer); |
064af421 BP |
764 | } |
765 | ||
064af421 | 766 | static void |
2b01925c BP |
767 | netdev_changed_cb(const struct rtnetlink_link_change *change, |
768 | void *aux OVS_UNUSED) | |
064af421 | 769 | { |
5326a0fd BP |
770 | char br_name[IFNAMSIZ]; |
771 | const char *port_name; | |
064af421 | 772 | |
5326a0fd | 773 | if (!change) { |
064af421 | 774 | VLOG_WARN_RL(&rl, "network monitor socket overflowed"); |
5326a0fd BP |
775 | return; |
776 | } | |
d295e8e9 | 777 | |
5326a0fd BP |
778 | if (change->nlmsg_type != RTM_DELLINK || !change->master_ifindex) { |
779 | return; | |
780 | } | |
064af421 | 781 | |
5326a0fd BP |
782 | port_name = change->ifname; |
783 | if (!if_indextoname(change->master_ifindex, br_name)) { | |
784 | return; | |
785 | } | |
b959290b | 786 | |
5326a0fd BP |
787 | VLOG_INFO("network device %s destroyed, removing from bridge %s", |
788 | port_name, br_name); | |
789 | ||
2b01925c | 790 | run_vsctl(vsctl_program, VSCTL_OPTIONS, |
9ed3ba29 | 791 | "--", "--if-exists", "del-port", port_name, |
2b01925c BP |
792 | "--", "comment", "ovs-brcompatd:", port_name, "disappeared", |
793 | (char *) NULL); | |
064af421 BP |
794 | } |
795 | ||
796 | int | |
797 | main(int argc, char *argv[]) | |
798 | { | |
480ce8ab | 799 | extern struct vlog_module VLM_reconnect; |
2ee6545f | 800 | struct nln_notifier *link_notifier; |
064af421 BP |
801 | struct unixctl_server *unixctl; |
802 | int retval; | |
803 | ||
40f0707c | 804 | proctitle_init(argc, argv); |
064af421 | 805 | set_program_name(argv[0]); |
480ce8ab | 806 | vlog_set_levels(&VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN); |
9852694f | 807 | |
2b01925c | 808 | parse_options(argc, argv); |
064af421 BP |
809 | signal(SIGPIPE, SIG_IGN); |
810 | process_init(); | |
811 | ||
95440284 | 812 | daemonize_start(); |
064af421 BP |
813 | |
814 | retval = unixctl_server_create(NULL, &unixctl); | |
815 | if (retval) { | |
4d12270a | 816 | exit(EXIT_FAILURE); |
064af421 BP |
817 | } |
818 | ||
819 | if (brc_open(&brc_sock)) { | |
279c9e03 BP |
820 | VLOG_FATAL("could not open brcompat socket. Check " |
821 | "\"brcompat\" kernel module."); | |
064af421 BP |
822 | } |
823 | ||
2ee6545f | 824 | link_notifier = rtnetlink_link_notifier_create(netdev_changed_cb, NULL); |
064af421 | 825 | |
95440284 BP |
826 | daemonize_complete(); |
827 | ||
064af421 BP |
828 | for (;;) { |
829 | unixctl_server_run(unixctl); | |
18a23781 | 830 | rtnetlink_link_run(); |
2b01925c | 831 | brc_recv_update(); |
5ff22a06 | 832 | |
8b61709d | 833 | netdev_run(); |
064af421 | 834 | |
064af421 BP |
835 | nl_sock_wait(brc_sock, POLLIN); |
836 | unixctl_server_wait(unixctl); | |
18a23781 | 837 | rtnetlink_link_wait(); |
8b61709d | 838 | netdev_wait(); |
064af421 BP |
839 | poll_block(); |
840 | } | |
841 | ||
2ee6545f | 842 | rtnetlink_link_notifier_destroy(link_notifier); |
9852694f | 843 | |
064af421 BP |
844 | return 0; |
845 | } | |
846 | ||
3c303e5f | 847 | static void |
064af421 BP |
848 | parse_options(int argc, char *argv[]) |
849 | { | |
850 | enum { | |
2b01925c BP |
851 | OPT_APPCTL, |
852 | OPT_VSCTL, | |
064af421 | 853 | VLOG_OPTION_ENUMS, |
8274ae95 BP |
854 | LEAK_CHECKER_OPTION_ENUMS, |
855 | DAEMON_OPTION_ENUMS | |
064af421 BP |
856 | }; |
857 | static struct option long_options[] = { | |
e3c17733 BP |
858 | {"help", no_argument, NULL, 'h'}, |
859 | {"version", no_argument, NULL, 'V'}, | |
2b01925c BP |
860 | {"appctl", required_argument, NULL, OPT_APPCTL}, |
861 | {"vsctl", required_argument, NULL, OPT_VSCTL}, | |
064af421 BP |
862 | DAEMON_LONG_OPTIONS, |
863 | VLOG_LONG_OPTIONS, | |
864 | LEAK_CHECKER_LONG_OPTIONS, | |
e3c17733 | 865 | {NULL, 0, NULL, 0}, |
064af421 BP |
866 | }; |
867 | char *short_options = long_options_to_short_options(long_options); | |
2b01925c BP |
868 | const char *appctl = "ovs-appctl"; |
869 | const char *vsctl = "ovs-vsctl"; | |
064af421 | 870 | |
064af421 BP |
871 | for (;;) { |
872 | int c; | |
873 | ||
874 | c = getopt_long(argc, argv, short_options, long_options, NULL); | |
875 | if (c == -1) { | |
876 | break; | |
877 | } | |
878 | ||
879 | switch (c) { | |
064af421 BP |
880 | case 'h': |
881 | usage(); | |
882 | ||
883 | case 'V': | |
55d5bb44 | 884 | ovs_print_version(0, 0); |
064af421 BP |
885 | exit(EXIT_SUCCESS); |
886 | ||
2b01925c BP |
887 | case OPT_APPCTL: |
888 | appctl = optarg; | |
889 | break; | |
890 | ||
891 | case OPT_VSCTL: | |
892 | vsctl = optarg; | |
064af421 BP |
893 | break; |
894 | ||
895 | VLOG_OPTION_HANDLERS | |
896 | DAEMON_OPTION_HANDLERS | |
897 | LEAK_CHECKER_OPTION_HANDLERS | |
898 | ||
899 | case '?': | |
900 | exit(EXIT_FAILURE); | |
901 | ||
902 | default: | |
903 | abort(); | |
904 | } | |
905 | } | |
906 | free(short_options); | |
907 | ||
2b01925c BP |
908 | appctl_program = process_search_path(appctl); |
909 | if (!appctl_program) { | |
910 | VLOG_FATAL("%s: not found in $PATH (use --appctl to specify an " | |
911 | "alternate location)", appctl); | |
912 | } | |
3c303e5f | 913 | |
2b01925c BP |
914 | vsctl_program = process_search_path(vsctl); |
915 | if (!vsctl_program) { | |
916 | VLOG_FATAL("%s: not found in $PATH (use --vsctl to specify an " | |
917 | "alternate location)", vsctl); | |
918 | } | |
064af421 | 919 | |
2b01925c BP |
920 | if (argc != optind) { |
921 | VLOG_FATAL("no non-option arguments are supported; " | |
279c9e03 | 922 | "use --help for usage"); |
064af421 | 923 | } |
064af421 BP |
924 | } |
925 | ||
926 | static void | |
927 | usage(void) | |
928 | { | |
929 | printf("%s: bridge compatibility front-end for ovs-vswitchd\n" | |
2b01925c | 930 | "usage: %s [OPTIONS]\n", |
064af421 BP |
931 | program_name, program_name); |
932 | printf("\nConfiguration options:\n" | |
2b01925c BP |
933 | " --appctl=PROGRAM overrides $PATH for finding ovs-appctl\n" |
934 | " --vsctl=PROGRAM overrides $PATH for finding ovs-vsctl\n" | |
064af421 BP |
935 | ); |
936 | daemon_usage(); | |
937 | vlog_usage(); | |
938 | printf("\nOther options:\n" | |
939 | " -h, --help display this help message\n" | |
940 | " -V, --version display version information\n"); | |
941 | leak_checker_usage(); | |
064af421 BP |
942 | exit(EXIT_SUCCESS); |
943 | } |