]>
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 BP |
73 | static int do_switching(struct switch_ *); |
74 | static void new_switch(struct switch_ *, struct vconn *, const char *name); | |
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 | } | |
111 | new_switch(&switches[n_switches++], vconn, name); | |
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) { | |
153 | new_switch(&switches[n_switches++], new_vconn, "tcp"); | |
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 | |
211 | new_switch(struct switch_ *sw, struct vconn *vconn, const char *name) | |
212 | { | |
213 | sw->rconn = rconn_new_from_vconn(name, vconn); | |
9af9e2e8 JT |
214 | sw->lswitch = lswitch_create(sw->rconn, learn_macs, exact_flows, |
215 | set_up_flows ? max_idle : -1, | |
216 | action_normal); | |
064af421 BP |
217 | } |
218 | ||
219 | static int | |
220 | do_switching(struct switch_ *sw) | |
221 | { | |
222 | unsigned int packets_sent; | |
223 | struct ofpbuf *msg; | |
224 | ||
225 | packets_sent = rconn_packets_sent(sw->rconn); | |
226 | ||
227 | msg = rconn_recv(sw->rconn); | |
228 | if (msg) { | |
7778bd15 BP |
229 | if (!mute) { |
230 | lswitch_process_packet(sw->lswitch, sw->rconn, msg); | |
231 | } | |
064af421 BP |
232 | ofpbuf_delete(msg); |
233 | } | |
234 | rconn_run(sw->rconn); | |
235 | ||
236 | return (!rconn_is_alive(sw->rconn) ? EOF | |
237 | : rconn_packets_sent(sw->rconn) != packets_sent ? 0 | |
238 | : EAGAIN); | |
239 | } | |
240 | ||
241 | static void | |
242 | parse_options(int argc, char *argv[]) | |
243 | { | |
244 | enum { | |
245 | OPT_MAX_IDLE = UCHAR_MAX + 1, | |
246 | OPT_PEER_CA_CERT, | |
7778bd15 | 247 | OPT_MUTE, |
b66bdf30 | 248 | OPT_UNIXCTL, |
064af421 BP |
249 | VLOG_OPTION_ENUMS |
250 | }; | |
251 | static struct option long_options[] = { | |
252 | {"hub", no_argument, 0, 'H'}, | |
253 | {"noflow", no_argument, 0, 'n'}, | |
9af9e2e8 JT |
254 | {"normal", no_argument, 0, 'N'}, |
255 | {"wildcard", no_argument, 0, 'w'}, | |
064af421 | 256 | {"max-idle", required_argument, 0, OPT_MAX_IDLE}, |
7778bd15 | 257 | {"mute", no_argument, 0, OPT_MUTE}, |
b66bdf30 | 258 | {"unixctl", required_argument, 0, OPT_UNIXCTL}, |
064af421 BP |
259 | {"help", no_argument, 0, 'h'}, |
260 | {"version", no_argument, 0, 'V'}, | |
261 | DAEMON_LONG_OPTIONS, | |
262 | VLOG_LONG_OPTIONS, | |
263 | #ifdef HAVE_OPENSSL | |
fe55ad15 | 264 | STREAM_SSL_LONG_OPTIONS |
064af421 BP |
265 | {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT}, |
266 | #endif | |
267 | {0, 0, 0, 0}, | |
268 | }; | |
269 | char *short_options = long_options_to_short_options(long_options); | |
270 | ||
271 | for (;;) { | |
272 | int indexptr; | |
273 | int c; | |
274 | ||
275 | c = getopt_long(argc, argv, short_options, long_options, &indexptr); | |
276 | if (c == -1) { | |
277 | break; | |
278 | } | |
279 | ||
280 | switch (c) { | |
281 | case 'H': | |
282 | learn_macs = false; | |
283 | break; | |
284 | ||
285 | case 'n': | |
d6fbec6d | 286 | set_up_flows = false; |
064af421 BP |
287 | break; |
288 | ||
7778bd15 BP |
289 | case OPT_MUTE: |
290 | mute = true; | |
291 | break; | |
292 | ||
9af9e2e8 JT |
293 | case 'N': |
294 | action_normal = true; | |
295 | break; | |
296 | ||
297 | case 'w': | |
298 | exact_flows = false; | |
299 | break; | |
300 | ||
064af421 BP |
301 | case OPT_MAX_IDLE: |
302 | if (!strcmp(optarg, "permanent")) { | |
303 | max_idle = OFP_FLOW_PERMANENT; | |
304 | } else { | |
305 | max_idle = atoi(optarg); | |
306 | if (max_idle < 1 || max_idle > 65535) { | |
307 | ovs_fatal(0, "--max-idle argument must be between 1 and " | |
308 | "65535 or the word 'permanent'"); | |
309 | } | |
310 | } | |
311 | break; | |
312 | ||
b66bdf30 BP |
313 | case OPT_UNIXCTL: |
314 | unixctl_path = optarg; | |
315 | break; | |
316 | ||
064af421 BP |
317 | case 'h': |
318 | usage(); | |
319 | ||
320 | case 'V': | |
321 | OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION); | |
322 | exit(EXIT_SUCCESS); | |
323 | ||
324 | VLOG_OPTION_HANDLERS | |
325 | DAEMON_OPTION_HANDLERS | |
326 | ||
327 | #ifdef HAVE_OPENSSL | |
fe55ad15 | 328 | STREAM_SSL_OPTION_HANDLERS |
064af421 BP |
329 | |
330 | case OPT_PEER_CA_CERT: | |
fe55ad15 | 331 | stream_ssl_set_peer_ca_cert_file(optarg); |
064af421 BP |
332 | break; |
333 | #endif | |
334 | ||
335 | case '?': | |
336 | exit(EXIT_FAILURE); | |
337 | ||
338 | default: | |
339 | abort(); | |
340 | } | |
341 | } | |
342 | free(short_options); | |
343 | } | |
344 | ||
345 | static void | |
346 | usage(void) | |
347 | { | |
348 | printf("%s: OpenFlow controller\n" | |
349 | "usage: %s [OPTIONS] METHOD\n" | |
350 | "where METHOD is any OpenFlow connection method.\n", | |
351 | program_name, program_name); | |
352 | vconn_usage(true, true, false); | |
353 | daemon_usage(); | |
354 | vlog_usage(); | |
355 | printf("\nOther options:\n" | |
356 | " -H, --hub act as hub instead of learning switch\n" | |
357 | " -n, --noflow pass traffic, but don't add flows\n" | |
358 | " --max-idle=SECS max idle time for new flows\n" | |
9af9e2e8 JT |
359 | " -N, --normal use OFPAT_NORMAL action\n" |
360 | " -w, --wildcard use wildcards, not exact-match rules\n" | |
b66bdf30 | 361 | " --unixctl=SOCKET override default control socket name\n" |
064af421 BP |
362 | " -h, --help display this help message\n" |
363 | " -V, --version display version information\n"); | |
364 | exit(EXIT_SUCCESS); | |
365 | } |