]>
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> | |
24 | #include <string.h> | |
25 | ||
26 | #include "command-line.h" | |
27 | #include "compiler.h" | |
28 | #include "daemon.h" | |
064af421 BP |
29 | #include "learning-switch.h" |
30 | #include "ofpbuf.h" | |
31 | #include "openflow/openflow.h" | |
32 | #include "poll-loop.h" | |
33 | #include "rconn.h" | |
fe55ad15 | 34 | #include "stream-ssl.h" |
064af421 BP |
35 | #include "timeval.h" |
36 | #include "unixctl.h" | |
37 | #include "util.h" | |
064af421 BP |
38 | #include "vconn.h" |
39 | ||
40 | #include "vlog.h" | |
41 | #define THIS_MODULE VLM_controller | |
42 | ||
43 | #define MAX_SWITCHES 16 | |
44 | #define MAX_LISTENERS 16 | |
45 | ||
46 | struct switch_ { | |
47 | struct lswitch *lswitch; | |
48 | struct rconn *rconn; | |
49 | }; | |
50 | ||
51 | /* Learn the ports on which MAC addresses appear? */ | |
52 | static bool learn_macs = true; | |
53 | ||
54 | /* Set up flows? (If not, every packet is processed at the controller.) */ | |
d6fbec6d | 55 | static bool set_up_flows = true; |
064af421 | 56 | |
9af9e2e8 JT |
57 | /* -N, --normal: Use "NORMAL" action instead of explicit port? */ |
58 | static bool action_normal = false; | |
59 | ||
60 | /* -w, --wildcard: Set up exact match or wildcard flow entries? */ | |
61 | static bool exact_flows = true; | |
62 | ||
064af421 BP |
63 | /* --max-idle: Maximum idle time, in seconds, before flows expire. */ |
64 | static int max_idle = 60; | |
65 | ||
7778bd15 BP |
66 | /* --mute: If true, accept connections from switches but do not reply to any |
67 | * of their messages (for debugging fail-open mode). */ | |
68 | static bool mute = false; | |
69 | ||
b66bdf30 BP |
70 | /* --unixctl: Name of unixctl socket, or null to use the default. */ |
71 | static char *unixctl_path = NULL; | |
72 | ||
064af421 | 73 | static int do_switching(struct switch_ *); |
58bdd092 | 74 | static void new_switch(struct switch_ *, struct vconn *); |
064af421 BP |
75 | static void parse_options(int argc, char *argv[]); |
76 | static void usage(void) NO_RETURN; | |
77 | ||
78 | int | |
79 | main(int argc, char *argv[]) | |
80 | { | |
81 | struct unixctl_server *unixctl; | |
82 | struct switch_ switches[MAX_SWITCHES]; | |
83 | struct pvconn *listeners[MAX_LISTENERS]; | |
84 | int n_switches, n_listeners; | |
85 | int retval; | |
86 | int i; | |
87 | ||
40f0707c | 88 | proctitle_init(argc, argv); |
064af421 | 89 | set_program_name(argv[0]); |
064af421 BP |
90 | time_init(); |
91 | vlog_init(); | |
92 | parse_options(argc, argv); | |
93 | signal(SIGPIPE, SIG_IGN); | |
94 | ||
95 | if (argc - optind < 1) { | |
96 | ovs_fatal(0, "at least one vconn argument required; " | |
97 | "use --help for usage"); | |
98 | } | |
99 | ||
100 | n_switches = n_listeners = 0; | |
101 | for (i = optind; i < argc; i++) { | |
102 | const char *name = argv[i]; | |
103 | struct vconn *vconn; | |
104 | int retval; | |
105 | ||
106 | retval = vconn_open(name, OFP_VERSION, &vconn); | |
107 | if (!retval) { | |
108 | if (n_switches >= MAX_SWITCHES) { | |
109 | ovs_fatal(0, "max %d switch connections", n_switches); | |
110 | } | |
58bdd092 | 111 | new_switch(&switches[n_switches++], vconn); |
064af421 BP |
112 | continue; |
113 | } else if (retval == EAFNOSUPPORT) { | |
114 | struct pvconn *pvconn; | |
115 | retval = pvconn_open(name, &pvconn); | |
116 | if (!retval) { | |
117 | if (n_listeners >= MAX_LISTENERS) { | |
118 | ovs_fatal(0, "max %d passive connections", n_listeners); | |
119 | } | |
120 | listeners[n_listeners++] = pvconn; | |
121 | } | |
122 | } | |
123 | if (retval) { | |
124 | VLOG_ERR("%s: connect: %s", name, strerror(retval)); | |
125 | } | |
126 | } | |
127 | if (n_switches == 0 && n_listeners == 0) { | |
128 | ovs_fatal(0, "no active or passive switch connections"); | |
129 | } | |
130 | ||
131 | die_if_already_running(); | |
95440284 | 132 | daemonize_start(); |
064af421 | 133 | |
b66bdf30 | 134 | retval = unixctl_server_create(unixctl_path, &unixctl); |
064af421 | 135 | if (retval) { |
4d12270a | 136 | exit(EXIT_FAILURE); |
064af421 BP |
137 | } |
138 | ||
95440284 BP |
139 | daemonize_complete(); |
140 | ||
064af421 BP |
141 | while (n_switches > 0 || n_listeners > 0) { |
142 | int iteration; | |
143 | int i; | |
144 | ||
145 | /* Accept connections on listening vconns. */ | |
146 | for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) { | |
147 | struct vconn *new_vconn; | |
148 | int retval; | |
149 | ||
150 | retval = pvconn_accept(listeners[i], OFP_VERSION, &new_vconn); | |
151 | if (!retval || retval == EAGAIN) { | |
152 | if (!retval) { | |
58bdd092 | 153 | new_switch(&switches[n_switches++], new_vconn); |
064af421 BP |
154 | } |
155 | i++; | |
156 | } else { | |
157 | pvconn_close(listeners[i]); | |
158 | listeners[i] = listeners[--n_listeners]; | |
159 | } | |
160 | } | |
161 | ||
162 | /* Do some switching work. Limit the number of iterations so that | |
163 | * callbacks registered with the poll loop don't starve. */ | |
164 | for (iteration = 0; iteration < 50; iteration++) { | |
165 | bool progress = false; | |
166 | for (i = 0; i < n_switches; ) { | |
167 | struct switch_ *this = &switches[i]; | |
168 | int retval = do_switching(this); | |
169 | if (!retval || retval == EAGAIN) { | |
170 | if (!retval) { | |
171 | progress = true; | |
172 | } | |
173 | i++; | |
174 | } else { | |
175 | rconn_destroy(this->rconn); | |
176 | lswitch_destroy(this->lswitch); | |
177 | switches[i] = switches[--n_switches]; | |
178 | } | |
179 | } | |
180 | if (!progress) { | |
181 | break; | |
182 | } | |
183 | } | |
184 | for (i = 0; i < n_switches; i++) { | |
185 | struct switch_ *this = &switches[i]; | |
186 | lswitch_run(this->lswitch, this->rconn); | |
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]; | |
199 | rconn_run_wait(sw->rconn); | |
200 | rconn_recv_wait(sw->rconn); | |
201 | lswitch_wait(sw->lswitch); | |
202 | } | |
203 | unixctl_server_wait(unixctl); | |
204 | poll_block(); | |
205 | } | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static void | |
58bdd092 | 211 | new_switch(struct switch_ *sw, struct vconn *vconn) |
064af421 | 212 | { |
9794e806 | 213 | sw->rconn = rconn_create(60, 0); |
eb15cdbb | 214 | rconn_connect_unreliably(sw->rconn, vconn, NULL); |
9af9e2e8 JT |
215 | sw->lswitch = lswitch_create(sw->rconn, learn_macs, exact_flows, |
216 | set_up_flows ? max_idle : -1, | |
217 | action_normal); | |
064af421 BP |
218 | } |
219 | ||
220 | static int | |
221 | do_switching(struct switch_ *sw) | |
222 | { | |
223 | unsigned int packets_sent; | |
224 | struct ofpbuf *msg; | |
225 | ||
226 | packets_sent = rconn_packets_sent(sw->rconn); | |
227 | ||
228 | msg = rconn_recv(sw->rconn); | |
229 | if (msg) { | |
7778bd15 BP |
230 | if (!mute) { |
231 | lswitch_process_packet(sw->lswitch, sw->rconn, msg); | |
232 | } | |
064af421 BP |
233 | ofpbuf_delete(msg); |
234 | } | |
235 | rconn_run(sw->rconn); | |
236 | ||
237 | return (!rconn_is_alive(sw->rconn) ? EOF | |
238 | : rconn_packets_sent(sw->rconn) != packets_sent ? 0 | |
239 | : EAGAIN); | |
240 | } | |
241 | ||
242 | static void | |
243 | parse_options(int argc, char *argv[]) | |
244 | { | |
245 | enum { | |
246 | OPT_MAX_IDLE = UCHAR_MAX + 1, | |
247 | OPT_PEER_CA_CERT, | |
7778bd15 | 248 | OPT_MUTE, |
b66bdf30 | 249 | OPT_UNIXCTL, |
064af421 BP |
250 | VLOG_OPTION_ENUMS |
251 | }; | |
252 | static struct option long_options[] = { | |
253 | {"hub", no_argument, 0, 'H'}, | |
254 | {"noflow", no_argument, 0, 'n'}, | |
9af9e2e8 JT |
255 | {"normal", no_argument, 0, 'N'}, |
256 | {"wildcard", no_argument, 0, 'w'}, | |
064af421 | 257 | {"max-idle", required_argument, 0, OPT_MAX_IDLE}, |
7778bd15 | 258 | {"mute", no_argument, 0, OPT_MUTE}, |
b66bdf30 | 259 | {"unixctl", required_argument, 0, OPT_UNIXCTL}, |
064af421 BP |
260 | {"help", no_argument, 0, 'h'}, |
261 | {"version", no_argument, 0, 'V'}, | |
262 | DAEMON_LONG_OPTIONS, | |
263 | VLOG_LONG_OPTIONS, | |
264 | #ifdef HAVE_OPENSSL | |
fe55ad15 | 265 | STREAM_SSL_LONG_OPTIONS |
064af421 BP |
266 | {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT}, |
267 | #endif | |
268 | {0, 0, 0, 0}, | |
269 | }; | |
270 | char *short_options = long_options_to_short_options(long_options); | |
271 | ||
272 | for (;;) { | |
273 | int indexptr; | |
274 | int c; | |
275 | ||
276 | c = getopt_long(argc, argv, short_options, long_options, &indexptr); | |
277 | if (c == -1) { | |
278 | break; | |
279 | } | |
280 | ||
281 | switch (c) { | |
282 | case 'H': | |
283 | learn_macs = false; | |
284 | break; | |
285 | ||
286 | case 'n': | |
d6fbec6d | 287 | set_up_flows = false; |
064af421 BP |
288 | break; |
289 | ||
7778bd15 BP |
290 | case OPT_MUTE: |
291 | mute = true; | |
292 | break; | |
293 | ||
9af9e2e8 JT |
294 | case 'N': |
295 | action_normal = true; | |
296 | break; | |
297 | ||
298 | case 'w': | |
299 | exact_flows = false; | |
300 | break; | |
301 | ||
064af421 BP |
302 | case OPT_MAX_IDLE: |
303 | if (!strcmp(optarg, "permanent")) { | |
304 | max_idle = OFP_FLOW_PERMANENT; | |
305 | } else { | |
306 | max_idle = atoi(optarg); | |
307 | if (max_idle < 1 || max_idle > 65535) { | |
308 | ovs_fatal(0, "--max-idle argument must be between 1 and " | |
309 | "65535 or the word 'permanent'"); | |
310 | } | |
311 | } | |
312 | break; | |
313 | ||
b66bdf30 BP |
314 | case OPT_UNIXCTL: |
315 | unixctl_path = optarg; | |
316 | break; | |
317 | ||
064af421 BP |
318 | case 'h': |
319 | usage(); | |
320 | ||
321 | case 'V': | |
322 | OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION); | |
323 | exit(EXIT_SUCCESS); | |
324 | ||
325 | VLOG_OPTION_HANDLERS | |
326 | DAEMON_OPTION_HANDLERS | |
327 | ||
328 | #ifdef HAVE_OPENSSL | |
fe55ad15 | 329 | STREAM_SSL_OPTION_HANDLERS |
064af421 BP |
330 | |
331 | case OPT_PEER_CA_CERT: | |
fe55ad15 | 332 | stream_ssl_set_peer_ca_cert_file(optarg); |
064af421 BP |
333 | break; |
334 | #endif | |
335 | ||
336 | case '?': | |
337 | exit(EXIT_FAILURE); | |
338 | ||
339 | default: | |
340 | abort(); | |
341 | } | |
342 | } | |
343 | free(short_options); | |
344 | } | |
345 | ||
346 | static void | |
347 | usage(void) | |
348 | { | |
349 | printf("%s: OpenFlow controller\n" | |
350 | "usage: %s [OPTIONS] METHOD\n" | |
351 | "where METHOD is any OpenFlow connection method.\n", | |
352 | program_name, program_name); | |
353 | vconn_usage(true, true, false); | |
354 | daemon_usage(); | |
355 | vlog_usage(); | |
356 | printf("\nOther options:\n" | |
357 | " -H, --hub act as hub instead of learning switch\n" | |
358 | " -n, --noflow pass traffic, but don't add flows\n" | |
359 | " --max-idle=SECS max idle time for new flows\n" | |
9af9e2e8 JT |
360 | " -N, --normal use OFPAT_NORMAL action\n" |
361 | " -w, --wildcard use wildcards, not exact-match rules\n" | |
b66bdf30 | 362 | " --unixctl=SOCKET override default control socket name\n" |
064af421 BP |
363 | " -h, --help display this help message\n" |
364 | " -V, --version display version information\n"); | |
365 | exit(EXIT_SUCCESS); | |
366 | } |