]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
50f96b10 | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015, 2017 Nicira, Inc. |
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" | |
8a777cf6 | 30 | #include "fatal-signal.h" |
064af421 | 31 | #include "learning-switch.h" |
be721d87 | 32 | #include "ofp-version-opt.h" |
64c96779 | 33 | #include "openvswitch/ofpbuf.h" |
064af421 | 34 | #include "openflow/openflow.h" |
fd016ae3 | 35 | #include "openvswitch/poll-loop.h" |
dc02e1eb | 36 | #include "openvswitch/rconn.h" |
44bac24b | 37 | #include "simap.h" |
fe55ad15 | 38 | #include "stream-ssl.h" |
064af421 BP |
39 | #include "timeval.h" |
40 | #include "unixctl.h" | |
41 | #include "util.h" | |
0d71302e | 42 | #include "openvswitch/ofp-flow.h" |
4a1f523f | 43 | #include "openvswitch/vconn.h" |
e6211adc | 44 | #include "openvswitch/vlog.h" |
f125905c | 45 | #include "socket-util.h" |
0d71302e | 46 | |
5136ce49 | 47 | |
d98e6007 | 48 | VLOG_DEFINE_THIS_MODULE(controller); |
064af421 BP |
49 | |
50 | #define MAX_SWITCHES 16 | |
51 | #define MAX_LISTENERS 16 | |
52 | ||
53 | struct switch_ { | |
54 | struct lswitch *lswitch; | |
064af421 BP |
55 | }; |
56 | ||
d4cdc6b4 | 57 | /* -H, --hub: Learn the ports on which MAC addresses appear? */ |
064af421 BP |
58 | static bool learn_macs = true; |
59 | ||
d4cdc6b4 BP |
60 | /* -n, --noflow: Set up flows? (If not, every packet is processed at the |
61 | * controller.) */ | |
d6fbec6d | 62 | static bool set_up_flows = true; |
064af421 | 63 | |
9af9e2e8 JT |
64 | /* -N, --normal: Use "NORMAL" action instead of explicit port? */ |
65 | static bool action_normal = false; | |
66 | ||
eec25dc1 | 67 | /* -w, --wildcard: 0 to disable wildcard flow entries, an OFPFW10_* bitmask to |
7286b1e1 BP |
68 | * enable specific wildcards, or UINT32_MAX to use the default wildcards. */ |
69 | static uint32_t wildcards = 0; | |
9af9e2e8 | 70 | |
064af421 BP |
71 | /* --max-idle: Maximum idle time, in seconds, before flows expire. */ |
72 | static int max_idle = 60; | |
73 | ||
7778bd15 BP |
74 | /* --mute: If true, accept connections from switches but do not reply to any |
75 | * of their messages (for debugging fail-open mode). */ | |
76 | static bool mute = false; | |
77 | ||
d4cdc6b4 BP |
78 | /* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */ |
79 | static uint32_t default_queue = UINT32_MAX; | |
80 | ||
44bac24b BP |
81 | /* -Q, --port-queue: map from port name to port number. */ |
82 | static struct simap port_queues = SIMAP_INITIALIZER(&port_queues); | |
611e9a35 | 83 | |
27527aa0 BP |
84 | /* --with-flows: Flows to send to switch. */ |
85 | static struct ofputil_flow_mod *default_flows; | |
86 | static size_t n_default_flows; | |
db0b6c29 | 87 | static enum ofputil_protocol usable_protocols; |
882c2399 | 88 | |
b66bdf30 BP |
89 | /* --unixctl: Name of unixctl socket, or null to use the default. */ |
90 | static char *unixctl_path = NULL; | |
91 | ||
58bdd092 | 92 | static void new_switch(struct switch_ *, struct vconn *); |
064af421 | 93 | static void parse_options(int argc, char *argv[]); |
cab50449 | 94 | OVS_NO_RETURN static void usage(void); |
064af421 BP |
95 | |
96 | int | |
97 | main(int argc, char *argv[]) | |
98 | { | |
99 | struct unixctl_server *unixctl; | |
100 | struct switch_ switches[MAX_SWITCHES]; | |
101 | struct pvconn *listeners[MAX_LISTENERS]; | |
102 | int n_switches, n_listeners; | |
103 | int retval; | |
104 | int i; | |
105 | ||
5f383751 | 106 | ovs_cmdl_proctitle_init(argc, argv); |
064af421 | 107 | set_program_name(argv[0]); |
fe559381 | 108 | service_start(&argc, &argv); |
064af421 | 109 | parse_options(argc, argv); |
8a777cf6 | 110 | fatal_ignore_sigpipe(); |
064af421 | 111 | |
e91b927d AZ |
112 | daemon_become_new_user(false); |
113 | ||
064af421 BP |
114 | if (argc - optind < 1) { |
115 | ovs_fatal(0, "at least one vconn argument required; " | |
116 | "use --help for usage"); | |
117 | } | |
118 | ||
119 | n_switches = n_listeners = 0; | |
120 | for (i = optind; i < argc; i++) { | |
121 | const char *name = argv[i]; | |
122 | struct vconn *vconn; | |
064af421 | 123 | |
82c8c53c BP |
124 | retval = vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, |
125 | &vconn); | |
064af421 BP |
126 | if (!retval) { |
127 | if (n_switches >= MAX_SWITCHES) { | |
128 | ovs_fatal(0, "max %d switch connections", n_switches); | |
129 | } | |
58bdd092 | 130 | new_switch(&switches[n_switches++], vconn); |
064af421 BP |
131 | continue; |
132 | } else if (retval == EAFNOSUPPORT) { | |
133 | struct pvconn *pvconn; | |
be721d87 | 134 | retval = pvconn_open(name, get_allowed_ofp_versions(), |
82c8c53c | 135 | DSCP_DEFAULT, &pvconn); |
064af421 BP |
136 | if (!retval) { |
137 | if (n_listeners >= MAX_LISTENERS) { | |
138 | ovs_fatal(0, "max %d passive connections", n_listeners); | |
139 | } | |
140 | listeners[n_listeners++] = pvconn; | |
141 | } | |
142 | } | |
143 | if (retval) { | |
10a89ef0 | 144 | VLOG_ERR("%s: connect: %s", name, ovs_strerror(retval)); |
064af421 BP |
145 | } |
146 | } | |
147 | if (n_switches == 0 && n_listeners == 0) { | |
148 | ovs_fatal(0, "no active or passive switch connections"); | |
149 | } | |
150 | ||
e91b927d | 151 | daemonize_start(false); |
064af421 | 152 | |
b66bdf30 | 153 | retval = unixctl_server_create(unixctl_path, &unixctl); |
064af421 | 154 | if (retval) { |
4d12270a | 155 | exit(EXIT_FAILURE); |
064af421 BP |
156 | } |
157 | ||
95440284 BP |
158 | daemonize_complete(); |
159 | ||
064af421 | 160 | while (n_switches > 0 || n_listeners > 0) { |
064af421 BP |
161 | /* Accept connections on listening vconns. */ |
162 | for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) { | |
163 | struct vconn *new_vconn; | |
064af421 | 164 | |
7a25bd99 | 165 | retval = pvconn_accept(listeners[i], &new_vconn); |
064af421 BP |
166 | if (!retval || retval == EAGAIN) { |
167 | if (!retval) { | |
58bdd092 | 168 | new_switch(&switches[n_switches++], new_vconn); |
064af421 BP |
169 | } |
170 | i++; | |
171 | } else { | |
172 | pvconn_close(listeners[i]); | |
173 | listeners[i] = listeners[--n_listeners]; | |
174 | } | |
175 | } | |
176 | ||
002c3f17 BP |
177 | /* Do some switching work. . */ |
178 | for (i = 0; i < n_switches; ) { | |
064af421 | 179 | struct switch_ *this = &switches[i]; |
ba186119 | 180 | lswitch_run(this->lswitch); |
002c3f17 BP |
181 | if (lswitch_is_alive(this->lswitch)) { |
182 | i++; | |
183 | } else { | |
184 | lswitch_destroy(this->lswitch); | |
185 | switches[i] = switches[--n_switches]; | |
186 | } | |
064af421 BP |
187 | } |
188 | ||
189 | unixctl_server_run(unixctl); | |
190 | ||
191 | /* Wait for something to happen. */ | |
192 | if (n_switches < MAX_SWITCHES) { | |
193 | for (i = 0; i < n_listeners; i++) { | |
194 | pvconn_wait(listeners[i]); | |
195 | } | |
196 | } | |
197 | for (i = 0; i < n_switches; i++) { | |
198 | struct switch_ *sw = &switches[i]; | |
064af421 BP |
199 | lswitch_wait(sw->lswitch); |
200 | } | |
201 | unixctl_server_wait(unixctl); | |
202 | poll_block(); | |
203 | } | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static void | |
58bdd092 | 209 | new_switch(struct switch_ *sw, struct vconn *vconn) |
064af421 | 210 | { |
ad67e568 | 211 | struct lswitch_config cfg; |
002c3f17 | 212 | struct rconn *rconn; |
ad67e568 | 213 | |
be721d87 | 214 | rconn = rconn_create(60, 0, DSCP_DEFAULT, get_allowed_ofp_versions()); |
002c3f17 | 215 | rconn_connect_unreliably(rconn, vconn, NULL); |
882c2399 | 216 | |
ad67e568 BP |
217 | cfg.mode = (action_normal ? LSW_NORMAL |
218 | : learn_macs ? LSW_LEARN | |
219 | : LSW_FLOOD); | |
7286b1e1 | 220 | cfg.wildcards = wildcards; |
ad67e568 | 221 | cfg.max_idle = set_up_flows ? max_idle : -1; |
27527aa0 BP |
222 | cfg.default_flows = default_flows; |
223 | cfg.n_default_flows = n_default_flows; | |
db0b6c29 | 224 | cfg.usable_protocols = usable_protocols; |
d4cdc6b4 BP |
225 | cfg.default_queue = default_queue; |
226 | cfg.port_queues = &port_queues; | |
002c3f17 BP |
227 | cfg.mute = mute; |
228 | sw->lswitch = lswitch_create(rconn, &cfg); | |
064af421 BP |
229 | } |
230 | ||
d4cdc6b4 BP |
231 | static void |
232 | add_port_queue(char *s) | |
233 | { | |
234 | char *save_ptr = NULL; | |
235 | char *port_name; | |
236 | char *queue_id; | |
237 | ||
238 | port_name = strtok_r(s, ":", &save_ptr); | |
239 | queue_id = strtok_r(NULL, "", &save_ptr); | |
240 | if (!queue_id) { | |
241 | ovs_fatal(0, "argument to -Q or --port-queue should take the form " | |
242 | "\"<port-name>:<queue-id>\""); | |
243 | } | |
244 | ||
44bac24b | 245 | if (!simap_put(&port_queues, port_name, atoi(queue_id))) { |
d4cdc6b4 BP |
246 | ovs_fatal(0, "<port-name> arguments for -Q or --port-queue must " |
247 | "be unique"); | |
248 | } | |
249 | } | |
250 | ||
064af421 BP |
251 | static void |
252 | parse_options(int argc, char *argv[]) | |
253 | { | |
254 | enum { | |
255 | OPT_MAX_IDLE = UCHAR_MAX + 1, | |
256 | OPT_PEER_CA_CERT, | |
7778bd15 | 257 | OPT_MUTE, |
882c2399 | 258 | OPT_WITH_FLOWS, |
b66bdf30 | 259 | OPT_UNIXCTL, |
8274ae95 | 260 | VLOG_OPTION_ENUMS, |
be721d87 | 261 | DAEMON_OPTION_ENUMS, |
e18a1d08 ER |
262 | OFP_VERSION_OPTION_ENUMS, |
263 | SSL_OPTION_ENUMS, | |
064af421 | 264 | }; |
07fc4ed3 | 265 | static const struct option long_options[] = { |
e3c17733 BP |
266 | {"hub", no_argument, NULL, 'H'}, |
267 | {"noflow", no_argument, NULL, 'n'}, | |
268 | {"normal", no_argument, NULL, 'N'}, | |
7286b1e1 | 269 | {"wildcards", optional_argument, NULL, 'w'}, |
e3c17733 BP |
270 | {"max-idle", required_argument, NULL, OPT_MAX_IDLE}, |
271 | {"mute", no_argument, NULL, OPT_MUTE}, | |
272 | {"queue", required_argument, NULL, 'q'}, | |
273 | {"port-queue", required_argument, NULL, 'Q'}, | |
274 | {"with-flows", required_argument, NULL, OPT_WITH_FLOWS}, | |
275 | {"unixctl", required_argument, NULL, OPT_UNIXCTL}, | |
276 | {"help", no_argument, NULL, 'h'}, | |
064af421 | 277 | DAEMON_LONG_OPTIONS, |
be721d87 | 278 | OFP_VERSION_LONG_OPTIONS, |
064af421 | 279 | VLOG_LONG_OPTIONS, |
bf8f2167 | 280 | STREAM_SSL_LONG_OPTIONS, |
e3c17733 BP |
281 | {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, |
282 | {NULL, 0, NULL, 0}, | |
064af421 | 283 | }; |
5f383751 | 284 | char *short_options = ovs_cmdl_long_options_to_short_options(long_options); |
064af421 BP |
285 | |
286 | for (;;) { | |
287 | int indexptr; | |
bdda5aca | 288 | char *error; |
064af421 BP |
289 | int c; |
290 | ||
291 | c = getopt_long(argc, argv, short_options, long_options, &indexptr); | |
292 | if (c == -1) { | |
293 | break; | |
294 | } | |
295 | ||
296 | switch (c) { | |
297 | case 'H': | |
298 | learn_macs = false; | |
299 | break; | |
300 | ||
301 | case 'n': | |
d6fbec6d | 302 | set_up_flows = false; |
064af421 BP |
303 | break; |
304 | ||
7778bd15 BP |
305 | case OPT_MUTE: |
306 | mute = true; | |
307 | break; | |
308 | ||
9af9e2e8 JT |
309 | case 'N': |
310 | action_normal = true; | |
311 | break; | |
312 | ||
313 | case 'w': | |
7286b1e1 | 314 | wildcards = optarg ? strtol(optarg, NULL, 16) : UINT32_MAX; |
9af9e2e8 JT |
315 | break; |
316 | ||
064af421 BP |
317 | case OPT_MAX_IDLE: |
318 | if (!strcmp(optarg, "permanent")) { | |
319 | max_idle = OFP_FLOW_PERMANENT; | |
320 | } else { | |
321 | max_idle = atoi(optarg); | |
322 | if (max_idle < 1 || max_idle > 65535) { | |
323 | ovs_fatal(0, "--max-idle argument must be between 1 and " | |
324 | "65535 or the word 'permanent'"); | |
325 | } | |
326 | } | |
327 | break; | |
328 | ||
611e9a35 | 329 | case 'q': |
d4cdc6b4 BP |
330 | default_queue = atoi(optarg); |
331 | break; | |
332 | ||
333 | case 'Q': | |
334 | add_port_queue(optarg); | |
611e9a35 BP |
335 | break; |
336 | ||
882c2399 | 337 | case OPT_WITH_FLOWS: |
4bc938cc | 338 | error = parse_ofp_flow_mod_file(optarg, NULL, NULL, OFPFC_ADD, |
50f96b10 | 339 | &default_flows, &n_default_flows, |
ba2fe8e9 | 340 | &usable_protocols); |
bdda5aca BP |
341 | if (error) { |
342 | ovs_fatal(0, "%s", error); | |
343 | } | |
882c2399 JP |
344 | break; |
345 | ||
b66bdf30 BP |
346 | case OPT_UNIXCTL: |
347 | unixctl_path = optarg; | |
348 | break; | |
349 | ||
064af421 BP |
350 | case 'h': |
351 | usage(); | |
352 | ||
064af421 | 353 | VLOG_OPTION_HANDLERS |
be721d87 | 354 | OFP_VERSION_OPTION_HANDLERS |
064af421 BP |
355 | DAEMON_OPTION_HANDLERS |
356 | ||
fe55ad15 | 357 | STREAM_SSL_OPTION_HANDLERS |
064af421 BP |
358 | |
359 | case OPT_PEER_CA_CERT: | |
fe55ad15 | 360 | stream_ssl_set_peer_ca_cert_file(optarg); |
064af421 | 361 | break; |
064af421 BP |
362 | |
363 | case '?': | |
364 | exit(EXIT_FAILURE); | |
365 | ||
366 | default: | |
367 | abort(); | |
368 | } | |
369 | } | |
370 | free(short_options); | |
d4cdc6b4 | 371 | |
44bac24b | 372 | if (!simap_is_empty(&port_queues) || default_queue != UINT32_MAX) { |
d4cdc6b4 BP |
373 | if (action_normal) { |
374 | ovs_error(0, "queue IDs are incompatible with -N or --normal; " | |
375 | "not using OFPP_NORMAL"); | |
376 | action_normal = false; | |
377 | } | |
378 | ||
379 | if (!learn_macs) { | |
380 | ovs_error(0, "queue IDs are incompatible with -H or --hub; " | |
381 | "not acting as hub"); | |
382 | learn_macs = true; | |
383 | } | |
384 | } | |
064af421 BP |
385 | } |
386 | ||
387 | static void | |
388 | usage(void) | |
389 | { | |
390 | printf("%s: OpenFlow controller\n" | |
391 | "usage: %s [OPTIONS] METHOD\n" | |
392 | "where METHOD is any OpenFlow connection method.\n", | |
393 | program_name, program_name); | |
394 | vconn_usage(true, true, false); | |
395 | daemon_usage(); | |
be721d87 | 396 | ofp_version_usage(); |
064af421 BP |
397 | vlog_usage(); |
398 | printf("\nOther options:\n" | |
399 | " -H, --hub act as hub instead of learning switch\n" | |
400 | " -n, --noflow pass traffic, but don't add flows\n" | |
401 | " --max-idle=SECS max idle time for new flows\n" | |
d4cdc6b4 | 402 | " -N, --normal use OFPP_NORMAL action\n" |
7286b1e1 | 403 | " -w, --wildcards[=MASK] wildcard (specified) bits in flows\n" |
d4cdc6b4 BP |
404 | " -q, --queue=QUEUE-ID OpenFlow queue ID to use for output\n" |
405 | " -Q PORT-NAME:QUEUE-ID use QUEUE-ID for frames from PORT-NAME\n" | |
882c2399 | 406 | " --with-flows FILE use the flows from FILE\n" |
b66bdf30 | 407 | " --unixctl=SOCKET override default control socket name\n" |
064af421 BP |
408 | " -h, --help display this help message\n" |
409 | " -V, --version display version information\n"); | |
410 | exit(EXIT_SUCCESS); | |
411 | } |