]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
4d12270a | 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 | ||
19 | #include <errno.h> | |
20 | #include <getopt.h> | |
21 | #include <limits.h> | |
22 | #include <signal.h> | |
23 | #include <stdlib.h> | |
882c2399 | 24 | #include <stdio.h> |
064af421 BP |
25 | #include <string.h> |
26 | ||
27 | #include "command-line.h" | |
28 | #include "compiler.h" | |
29 | #include "daemon.h" | |
064af421 | 30 | #include "learning-switch.h" |
09913dfd | 31 | #include "ofp-parse.h" |
064af421 BP |
32 | #include "ofpbuf.h" |
33 | #include "openflow/openflow.h" | |
34 | #include "poll-loop.h" | |
35 | #include "rconn.h" | |
d4cdc6b4 | 36 | #include "shash.h" |
fe55ad15 | 37 | #include "stream-ssl.h" |
064af421 BP |
38 | #include "timeval.h" |
39 | #include "unixctl.h" | |
40 | #include "util.h" | |
064af421 | 41 | #include "vconn.h" |
064af421 | 42 | #include "vlog.h" |
5136ce49 | 43 | |
d98e6007 | 44 | VLOG_DEFINE_THIS_MODULE(controller); |
064af421 BP |
45 | |
46 | #define MAX_SWITCHES 16 | |
47 | #define MAX_LISTENERS 16 | |
48 | ||
49 | struct switch_ { | |
50 | struct lswitch *lswitch; | |
51 | struct rconn *rconn; | |
52 | }; | |
53 | ||
d4cdc6b4 | 54 | /* -H, --hub: Learn the ports on which MAC addresses appear? */ |
064af421 BP |
55 | static bool learn_macs = true; |
56 | ||
d4cdc6b4 BP |
57 | /* -n, --noflow: Set up flows? (If not, every packet is processed at the |
58 | * controller.) */ | |
d6fbec6d | 59 | static bool set_up_flows = true; |
064af421 | 60 | |
9af9e2e8 JT |
61 | /* -N, --normal: Use "NORMAL" action instead of explicit port? */ |
62 | static bool action_normal = false; | |
63 | ||
64 | /* -w, --wildcard: Set up exact match or wildcard flow entries? */ | |
65 | static bool exact_flows = true; | |
66 | ||
064af421 BP |
67 | /* --max-idle: Maximum idle time, in seconds, before flows expire. */ |
68 | static int max_idle = 60; | |
69 | ||
7778bd15 BP |
70 | /* --mute: If true, accept connections from switches but do not reply to any |
71 | * of their messages (for debugging fail-open mode). */ | |
72 | static bool mute = false; | |
73 | ||
d4cdc6b4 BP |
74 | /* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */ |
75 | static uint32_t default_queue = UINT32_MAX; | |
76 | ||
77 | /* -Q, --port-queue: map from port name to port number (cast to void *). */ | |
78 | static struct shash port_queues = SHASH_INITIALIZER(&port_queues); | |
611e9a35 | 79 | |
b3907fbc BP |
80 | /* --with-flows: Flows to send to switch, or an empty list not to send any |
81 | * default flows. */ | |
82 | static struct list default_flows = LIST_INITIALIZER(&default_flows); | |
882c2399 | 83 | |
b66bdf30 BP |
84 | /* --unixctl: Name of unixctl socket, or null to use the default. */ |
85 | static char *unixctl_path = NULL; | |
86 | ||
064af421 | 87 | static int do_switching(struct switch_ *); |
58bdd092 | 88 | static void new_switch(struct switch_ *, struct vconn *); |
064af421 BP |
89 | static void parse_options(int argc, char *argv[]); |
90 | static void usage(void) NO_RETURN; | |
91 | ||
92 | int | |
93 | main(int argc, char *argv[]) | |
94 | { | |
95 | struct unixctl_server *unixctl; | |
96 | struct switch_ switches[MAX_SWITCHES]; | |
97 | struct pvconn *listeners[MAX_LISTENERS]; | |
98 | int n_switches, n_listeners; | |
99 | int retval; | |
100 | int i; | |
101 | ||
40f0707c | 102 | proctitle_init(argc, argv); |
064af421 | 103 | set_program_name(argv[0]); |
064af421 BP |
104 | parse_options(argc, argv); |
105 | signal(SIGPIPE, SIG_IGN); | |
106 | ||
107 | if (argc - optind < 1) { | |
108 | ovs_fatal(0, "at least one vconn argument required; " | |
109 | "use --help for usage"); | |
110 | } | |
111 | ||
112 | n_switches = n_listeners = 0; | |
113 | for (i = optind; i < argc; i++) { | |
114 | const char *name = argv[i]; | |
115 | struct vconn *vconn; | |
064af421 BP |
116 | |
117 | retval = vconn_open(name, OFP_VERSION, &vconn); | |
118 | if (!retval) { | |
119 | if (n_switches >= MAX_SWITCHES) { | |
120 | ovs_fatal(0, "max %d switch connections", n_switches); | |
121 | } | |
58bdd092 | 122 | new_switch(&switches[n_switches++], vconn); |
064af421 BP |
123 | continue; |
124 | } else if (retval == EAFNOSUPPORT) { | |
125 | struct pvconn *pvconn; | |
126 | retval = pvconn_open(name, &pvconn); | |
127 | if (!retval) { | |
128 | if (n_listeners >= MAX_LISTENERS) { | |
129 | ovs_fatal(0, "max %d passive connections", n_listeners); | |
130 | } | |
131 | listeners[n_listeners++] = pvconn; | |
132 | } | |
133 | } | |
134 | if (retval) { | |
135 | VLOG_ERR("%s: connect: %s", name, strerror(retval)); | |
136 | } | |
137 | } | |
138 | if (n_switches == 0 && n_listeners == 0) { | |
139 | ovs_fatal(0, "no active or passive switch connections"); | |
140 | } | |
141 | ||
142 | die_if_already_running(); | |
95440284 | 143 | daemonize_start(); |
064af421 | 144 | |
b66bdf30 | 145 | retval = unixctl_server_create(unixctl_path, &unixctl); |
064af421 | 146 | if (retval) { |
4d12270a | 147 | exit(EXIT_FAILURE); |
064af421 BP |
148 | } |
149 | ||
95440284 BP |
150 | daemonize_complete(); |
151 | ||
064af421 BP |
152 | while (n_switches > 0 || n_listeners > 0) { |
153 | int iteration; | |
064af421 BP |
154 | |
155 | /* Accept connections on listening vconns. */ | |
156 | for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) { | |
157 | struct vconn *new_vconn; | |
064af421 BP |
158 | |
159 | retval = pvconn_accept(listeners[i], OFP_VERSION, &new_vconn); | |
160 | if (!retval || retval == EAGAIN) { | |
161 | if (!retval) { | |
58bdd092 | 162 | new_switch(&switches[n_switches++], new_vconn); |
064af421 BP |
163 | } |
164 | i++; | |
165 | } else { | |
166 | pvconn_close(listeners[i]); | |
167 | listeners[i] = listeners[--n_listeners]; | |
168 | } | |
169 | } | |
170 | ||
171 | /* Do some switching work. Limit the number of iterations so that | |
172 | * callbacks registered with the poll loop don't starve. */ | |
173 | for (iteration = 0; iteration < 50; iteration++) { | |
174 | bool progress = false; | |
175 | for (i = 0; i < n_switches; ) { | |
176 | struct switch_ *this = &switches[i]; | |
2a022368 BP |
177 | |
178 | retval = do_switching(this); | |
064af421 BP |
179 | if (!retval || retval == EAGAIN) { |
180 | if (!retval) { | |
181 | progress = true; | |
182 | } | |
183 | i++; | |
184 | } else { | |
185 | rconn_destroy(this->rconn); | |
186 | lswitch_destroy(this->lswitch); | |
187 | switches[i] = switches[--n_switches]; | |
188 | } | |
189 | } | |
190 | if (!progress) { | |
191 | break; | |
192 | } | |
193 | } | |
194 | for (i = 0; i < n_switches; i++) { | |
195 | struct switch_ *this = &switches[i]; | |
ba186119 | 196 | lswitch_run(this->lswitch); |
064af421 BP |
197 | } |
198 | ||
199 | unixctl_server_run(unixctl); | |
200 | ||
201 | /* Wait for something to happen. */ | |
202 | if (n_switches < MAX_SWITCHES) { | |
203 | for (i = 0; i < n_listeners; i++) { | |
204 | pvconn_wait(listeners[i]); | |
205 | } | |
206 | } | |
207 | for (i = 0; i < n_switches; i++) { | |
208 | struct switch_ *sw = &switches[i]; | |
209 | rconn_run_wait(sw->rconn); | |
210 | rconn_recv_wait(sw->rconn); | |
211 | lswitch_wait(sw->lswitch); | |
212 | } | |
213 | unixctl_server_wait(unixctl); | |
214 | poll_block(); | |
215 | } | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | static void | |
58bdd092 | 221 | new_switch(struct switch_ *sw, struct vconn *vconn) |
064af421 | 222 | { |
ad67e568 BP |
223 | struct lswitch_config cfg; |
224 | ||
9794e806 | 225 | sw->rconn = rconn_create(60, 0); |
eb15cdbb | 226 | rconn_connect_unreliably(sw->rconn, vconn, NULL); |
882c2399 | 227 | |
ad67e568 BP |
228 | cfg.mode = (action_normal ? LSW_NORMAL |
229 | : learn_macs ? LSW_LEARN | |
230 | : LSW_FLOOD); | |
231 | cfg.max_idle = set_up_flows ? max_idle : -1; | |
b3907fbc | 232 | cfg.default_flows = &default_flows; |
d4cdc6b4 BP |
233 | cfg.default_queue = default_queue; |
234 | cfg.port_queues = &port_queues; | |
ad67e568 | 235 | sw->lswitch = lswitch_create(sw->rconn, &cfg); |
064af421 BP |
236 | } |
237 | ||
238 | static int | |
239 | do_switching(struct switch_ *sw) | |
240 | { | |
241 | unsigned int packets_sent; | |
242 | struct ofpbuf *msg; | |
243 | ||
244 | packets_sent = rconn_packets_sent(sw->rconn); | |
245 | ||
246 | msg = rconn_recv(sw->rconn); | |
247 | if (msg) { | |
7778bd15 BP |
248 | if (!mute) { |
249 | lswitch_process_packet(sw->lswitch, sw->rconn, msg); | |
250 | } | |
064af421 BP |
251 | ofpbuf_delete(msg); |
252 | } | |
253 | rconn_run(sw->rconn); | |
254 | ||
255 | return (!rconn_is_alive(sw->rconn) ? EOF | |
256 | : rconn_packets_sent(sw->rconn) != packets_sent ? 0 | |
257 | : EAGAIN); | |
258 | } | |
259 | ||
09913dfd BP |
260 | static void |
261 | read_flow_file(const char *name) | |
262 | { | |
88ca35ee | 263 | enum nx_flow_format flow_format; |
09913dfd BP |
264 | FILE *stream; |
265 | ||
266 | stream = fopen(optarg, "r"); | |
267 | if (!stream) { | |
268 | ovs_fatal(errno, "%s: open", name); | |
269 | } | |
270 | ||
88ca35ee BP |
271 | flow_format = NXFF_OPENFLOW10; |
272 | while (parse_ofp_add_flow_file(&default_flows, &flow_format, stream)) { | |
273 | continue; | |
09913dfd BP |
274 | } |
275 | ||
276 | fclose(stream); | |
277 | } | |
278 | ||
d4cdc6b4 BP |
279 | static void |
280 | add_port_queue(char *s) | |
281 | { | |
282 | char *save_ptr = NULL; | |
283 | char *port_name; | |
284 | char *queue_id; | |
285 | ||
286 | port_name = strtok_r(s, ":", &save_ptr); | |
287 | queue_id = strtok_r(NULL, "", &save_ptr); | |
288 | if (!queue_id) { | |
289 | ovs_fatal(0, "argument to -Q or --port-queue should take the form " | |
290 | "\"<port-name>:<queue-id>\""); | |
291 | } | |
292 | ||
293 | if (!shash_add_once(&port_queues, port_name, | |
294 | (void *) (uintptr_t) atoi(queue_id))) { | |
295 | ovs_fatal(0, "<port-name> arguments for -Q or --port-queue must " | |
296 | "be unique"); | |
297 | } | |
298 | } | |
299 | ||
064af421 BP |
300 | static void |
301 | parse_options(int argc, char *argv[]) | |
302 | { | |
303 | enum { | |
304 | OPT_MAX_IDLE = UCHAR_MAX + 1, | |
305 | OPT_PEER_CA_CERT, | |
7778bd15 | 306 | OPT_MUTE, |
882c2399 | 307 | OPT_WITH_FLOWS, |
b66bdf30 | 308 | OPT_UNIXCTL, |
064af421 BP |
309 | VLOG_OPTION_ENUMS |
310 | }; | |
311 | static struct option long_options[] = { | |
312 | {"hub", no_argument, 0, 'H'}, | |
313 | {"noflow", no_argument, 0, 'n'}, | |
9af9e2e8 JT |
314 | {"normal", no_argument, 0, 'N'}, |
315 | {"wildcard", no_argument, 0, 'w'}, | |
064af421 | 316 | {"max-idle", required_argument, 0, OPT_MAX_IDLE}, |
7778bd15 | 317 | {"mute", no_argument, 0, OPT_MUTE}, |
611e9a35 | 318 | {"queue", required_argument, 0, 'q'}, |
d4cdc6b4 | 319 | {"port-queue", required_argument, 0, 'Q'}, |
882c2399 | 320 | {"with-flows", required_argument, 0, OPT_WITH_FLOWS}, |
b66bdf30 | 321 | {"unixctl", required_argument, 0, OPT_UNIXCTL}, |
064af421 BP |
322 | {"help", no_argument, 0, 'h'}, |
323 | {"version", no_argument, 0, 'V'}, | |
324 | DAEMON_LONG_OPTIONS, | |
325 | VLOG_LONG_OPTIONS, | |
326 | #ifdef HAVE_OPENSSL | |
fe55ad15 | 327 | STREAM_SSL_LONG_OPTIONS |
064af421 BP |
328 | {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT}, |
329 | #endif | |
330 | {0, 0, 0, 0}, | |
331 | }; | |
332 | char *short_options = long_options_to_short_options(long_options); | |
333 | ||
334 | for (;;) { | |
335 | int indexptr; | |
336 | int c; | |
337 | ||
338 | c = getopt_long(argc, argv, short_options, long_options, &indexptr); | |
339 | if (c == -1) { | |
340 | break; | |
341 | } | |
342 | ||
343 | switch (c) { | |
344 | case 'H': | |
345 | learn_macs = false; | |
346 | break; | |
347 | ||
348 | case 'n': | |
d6fbec6d | 349 | set_up_flows = false; |
064af421 BP |
350 | break; |
351 | ||
7778bd15 BP |
352 | case OPT_MUTE: |
353 | mute = true; | |
354 | break; | |
355 | ||
9af9e2e8 JT |
356 | case 'N': |
357 | action_normal = true; | |
358 | break; | |
359 | ||
360 | case 'w': | |
361 | exact_flows = false; | |
362 | break; | |
363 | ||
064af421 BP |
364 | case OPT_MAX_IDLE: |
365 | if (!strcmp(optarg, "permanent")) { | |
366 | max_idle = OFP_FLOW_PERMANENT; | |
367 | } else { | |
368 | max_idle = atoi(optarg); | |
369 | if (max_idle < 1 || max_idle > 65535) { | |
370 | ovs_fatal(0, "--max-idle argument must be between 1 and " | |
371 | "65535 or the word 'permanent'"); | |
372 | } | |
373 | } | |
374 | break; | |
375 | ||
611e9a35 | 376 | case 'q': |
d4cdc6b4 BP |
377 | default_queue = atoi(optarg); |
378 | break; | |
379 | ||
380 | case 'Q': | |
381 | add_port_queue(optarg); | |
611e9a35 BP |
382 | break; |
383 | ||
882c2399 | 384 | case OPT_WITH_FLOWS: |
09913dfd | 385 | read_flow_file(optarg); |
882c2399 JP |
386 | break; |
387 | ||
b66bdf30 BP |
388 | case OPT_UNIXCTL: |
389 | unixctl_path = optarg; | |
390 | break; | |
391 | ||
064af421 BP |
392 | case 'h': |
393 | usage(); | |
394 | ||
395 | case 'V': | |
396 | OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION); | |
397 | exit(EXIT_SUCCESS); | |
398 | ||
399 | VLOG_OPTION_HANDLERS | |
400 | DAEMON_OPTION_HANDLERS | |
401 | ||
402 | #ifdef HAVE_OPENSSL | |
fe55ad15 | 403 | STREAM_SSL_OPTION_HANDLERS |
064af421 BP |
404 | |
405 | case OPT_PEER_CA_CERT: | |
fe55ad15 | 406 | stream_ssl_set_peer_ca_cert_file(optarg); |
064af421 BP |
407 | break; |
408 | #endif | |
409 | ||
410 | case '?': | |
411 | exit(EXIT_FAILURE); | |
412 | ||
413 | default: | |
414 | abort(); | |
415 | } | |
416 | } | |
417 | free(short_options); | |
d4cdc6b4 BP |
418 | |
419 | if (!shash_is_empty(&port_queues) || default_queue != UINT32_MAX) { | |
420 | if (action_normal) { | |
421 | ovs_error(0, "queue IDs are incompatible with -N or --normal; " | |
422 | "not using OFPP_NORMAL"); | |
423 | action_normal = false; | |
424 | } | |
425 | ||
426 | if (!learn_macs) { | |
427 | ovs_error(0, "queue IDs are incompatible with -H or --hub; " | |
428 | "not acting as hub"); | |
429 | learn_macs = true; | |
430 | } | |
431 | } | |
064af421 BP |
432 | } |
433 | ||
434 | static void | |
435 | usage(void) | |
436 | { | |
437 | printf("%s: OpenFlow controller\n" | |
438 | "usage: %s [OPTIONS] METHOD\n" | |
439 | "where METHOD is any OpenFlow connection method.\n", | |
440 | program_name, program_name); | |
441 | vconn_usage(true, true, false); | |
442 | daemon_usage(); | |
443 | vlog_usage(); | |
444 | printf("\nOther options:\n" | |
445 | " -H, --hub act as hub instead of learning switch\n" | |
446 | " -n, --noflow pass traffic, but don't add flows\n" | |
447 | " --max-idle=SECS max idle time for new flows\n" | |
d4cdc6b4 | 448 | " -N, --normal use OFPP_NORMAL action\n" |
9af9e2e8 | 449 | " -w, --wildcard use wildcards, not exact-match rules\n" |
d4cdc6b4 BP |
450 | " -q, --queue=QUEUE-ID OpenFlow queue ID to use for output\n" |
451 | " -Q PORT-NAME:QUEUE-ID use QUEUE-ID for frames from PORT-NAME\n" | |
882c2399 | 452 | " --with-flows FILE use the flows from FILE\n" |
b66bdf30 | 453 | " --unixctl=SOCKET override default control socket name\n" |
064af421 BP |
454 | " -h, --help display this help message\n" |
455 | " -V, --version display version information\n"); | |
456 | exit(EXIT_SUCCESS); | |
457 | } |