]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
922fed06 | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2016 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> | |
064af421 BP |
18 | #include <errno.h> |
19 | #include <poll.h> | |
20 | #include <stdlib.h> | |
21 | #include <string.h> | |
22 | #include <sys/types.h> | |
23 | #include <unistd.h> | |
56192221 | 24 | #include "fatal-signal.h" |
64c96779 | 25 | #include "openvswitch/ofpbuf.h" |
064af421 BP |
26 | #include "openflow/openflow.h" |
27 | #include "poll-loop.h" | |
28 | #include "socket-util.h" | |
fe55ad15 | 29 | #include "stream.h" |
064af421 BP |
30 | #include "util.h" |
31 | #include "vconn-provider.h" | |
4a1f523f | 32 | #include "openvswitch/vconn.h" |
e6211adc | 33 | #include "openvswitch/vlog.h" |
5136ce49 | 34 | |
d98e6007 | 35 | VLOG_DEFINE_THIS_MODULE(vconn_stream); |
064af421 BP |
36 | |
37 | /* Active stream socket vconn. */ | |
38 | ||
fe55ad15 | 39 | struct vconn_stream |
064af421 BP |
40 | { |
41 | struct vconn vconn; | |
fe55ad15 | 42 | struct stream *stream; |
064af421 BP |
43 | struct ofpbuf *rxbuf; |
44 | struct ofpbuf *txbuf; | |
1e3c0047 | 45 | int n_packets; |
064af421 BP |
46 | }; |
47 | ||
5b712636 | 48 | static const struct vconn_class stream_vconn_class; |
064af421 BP |
49 | |
50 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); | |
51 | ||
fe55ad15 | 52 | static void vconn_stream_clear_txbuf(struct vconn_stream *); |
064af421 | 53 | |
fe55ad15 | 54 | static struct vconn * |
7a25bd99 SH |
55 | vconn_stream_new(struct stream *stream, int connect_status, |
56 | uint32_t allowed_versions) | |
064af421 | 57 | { |
fe55ad15 | 58 | struct vconn_stream *s; |
064af421 BP |
59 | |
60 | s = xmalloc(sizeof *s); | |
fe55ad15 | 61 | vconn_init(&s->vconn, &stream_vconn_class, connect_status, |
7a25bd99 | 62 | stream_get_name(stream), allowed_versions); |
fe55ad15 | 63 | s->stream = stream; |
064af421 | 64 | s->txbuf = NULL; |
064af421 | 65 | s->rxbuf = NULL; |
1e3c0047 | 66 | s->n_packets = 0; |
fe55ad15 BP |
67 | return &s->vconn; |
68 | } | |
69 | ||
70 | /* Creates a new vconn that will send and receive data on a stream named 'name' | |
71 | * and stores a pointer to the vconn in '*vconnp'. | |
72 | * | |
73 | * Returns 0 if successful, otherwise a positive errno value. */ | |
74 | static int | |
7a25bd99 SH |
75 | vconn_stream_open(const char *name, uint32_t allowed_versions, |
76 | char *suffix OVS_UNUSED, struct vconn **vconnp, uint8_t dscp) | |
fe55ad15 BP |
77 | { |
78 | struct stream *stream; | |
fe55ad15 BP |
79 | int error; |
80 | ||
d4763d1d | 81 | error = stream_open_with_default_port(name, OFP_PORT, &stream, dscp); |
b0bfeb3e BP |
82 | if (!error) { |
83 | error = stream_connect(stream); | |
84 | if (!error || error == EAGAIN) { | |
7a25bd99 | 85 | *vconnp = vconn_stream_new(stream, error, allowed_versions); |
b0bfeb3e BP |
86 | return 0; |
87 | } | |
fe55ad15 BP |
88 | } |
89 | ||
b0bfeb3e BP |
90 | stream_close(stream); |
91 | return error; | |
064af421 BP |
92 | } |
93 | ||
fe55ad15 BP |
94 | static struct vconn_stream * |
95 | vconn_stream_cast(struct vconn *vconn) | |
064af421 | 96 | { |
fe55ad15 | 97 | return CONTAINER_OF(vconn, struct vconn_stream, vconn); |
064af421 BP |
98 | } |
99 | ||
100 | static void | |
fe55ad15 | 101 | vconn_stream_close(struct vconn *vconn) |
064af421 | 102 | { |
fe55ad15 | 103 | struct vconn_stream *s = vconn_stream_cast(vconn); |
1e3c0047 BP |
104 | |
105 | if ((vconn->error == EPROTO || s->n_packets < 1) && s->rxbuf) { | |
6fd6ed71 | 106 | stream_report_content(s->rxbuf->data, s->rxbuf->size, STREAM_OPENFLOW, |
922fed06 | 107 | &this_module, vconn_get_name(vconn)); |
1e3c0047 BP |
108 | } |
109 | ||
fe55ad15 BP |
110 | stream_close(s->stream); |
111 | vconn_stream_clear_txbuf(s); | |
064af421 | 112 | ofpbuf_delete(s->rxbuf); |
064af421 BP |
113 | free(s); |
114 | } | |
115 | ||
116 | static int | |
fe55ad15 | 117 | vconn_stream_connect(struct vconn *vconn) |
064af421 | 118 | { |
fe55ad15 BP |
119 | struct vconn_stream *s = vconn_stream_cast(vconn); |
120 | return stream_connect(s->stream); | |
064af421 BP |
121 | } |
122 | ||
123 | static int | |
c088c3be | 124 | vconn_stream_recv__(struct vconn_stream *s, int rx_len) |
064af421 | 125 | { |
c088c3be BP |
126 | struct ofpbuf *rx = s->rxbuf; |
127 | int want_bytes, retval; | |
064af421 | 128 | |
6fd6ed71 | 129 | want_bytes = rx_len - rx->size; |
064af421 | 130 | ofpbuf_prealloc_tailroom(rx, want_bytes); |
fe55ad15 | 131 | retval = stream_recv(s->stream, ofpbuf_tail(rx), want_bytes); |
064af421 | 132 | if (retval > 0) { |
6fd6ed71 | 133 | rx->size += retval; |
c088c3be | 134 | return retval == want_bytes ? 0 : EAGAIN; |
064af421 | 135 | } else if (retval == 0) { |
6fd6ed71 | 136 | if (rx->size) { |
064af421 BP |
137 | VLOG_ERR_RL(&rl, "connection dropped mid-packet"); |
138 | return EPROTO; | |
064af421 | 139 | } |
c088c3be | 140 | return EOF; |
064af421 | 141 | } else { |
fe55ad15 | 142 | return -retval; |
064af421 BP |
143 | } |
144 | } | |
145 | ||
c088c3be BP |
146 | static int |
147 | vconn_stream_recv(struct vconn *vconn, struct ofpbuf **bufferp) | |
148 | { | |
149 | struct vconn_stream *s = vconn_stream_cast(vconn); | |
150 | const struct ofp_header *oh; | |
151 | int rx_len; | |
152 | ||
153 | /* Allocate new receive buffer if we don't have one. */ | |
154 | if (s->rxbuf == NULL) { | |
155 | s->rxbuf = ofpbuf_new(1564); | |
156 | } | |
157 | ||
158 | /* Read ofp_header. */ | |
6fd6ed71 | 159 | if (s->rxbuf->size < sizeof(struct ofp_header)) { |
c088c3be BP |
160 | int retval = vconn_stream_recv__(s, sizeof(struct ofp_header)); |
161 | if (retval) { | |
162 | return retval; | |
163 | } | |
164 | } | |
165 | ||
166 | /* Read payload. */ | |
6fd6ed71 | 167 | oh = s->rxbuf->data; |
c088c3be BP |
168 | rx_len = ntohs(oh->length); |
169 | if (rx_len < sizeof(struct ofp_header)) { | |
b56042ac | 170 | VLOG_ERR_RL(&rl, "received too-short ofp_header (%d bytes)", rx_len); |
c088c3be | 171 | return EPROTO; |
6fd6ed71 | 172 | } else if (s->rxbuf->size < rx_len) { |
c088c3be BP |
173 | int retval = vconn_stream_recv__(s, rx_len); |
174 | if (retval) { | |
175 | return retval; | |
176 | } | |
177 | } | |
178 | ||
1e3c0047 | 179 | s->n_packets++; |
c088c3be BP |
180 | *bufferp = s->rxbuf; |
181 | s->rxbuf = NULL; | |
182 | return 0; | |
183 | } | |
184 | ||
064af421 | 185 | static void |
fe55ad15 | 186 | vconn_stream_clear_txbuf(struct vconn_stream *s) |
064af421 BP |
187 | { |
188 | ofpbuf_delete(s->txbuf); | |
189 | s->txbuf = NULL; | |
064af421 BP |
190 | } |
191 | ||
192 | static int | |
fe55ad15 | 193 | vconn_stream_send(struct vconn *vconn, struct ofpbuf *buffer) |
064af421 | 194 | { |
fe55ad15 | 195 | struct vconn_stream *s = vconn_stream_cast(vconn); |
064af421 BP |
196 | ssize_t retval; |
197 | ||
198 | if (s->txbuf) { | |
199 | return EAGAIN; | |
200 | } | |
201 | ||
6fd6ed71 PS |
202 | retval = stream_send(s->stream, buffer->data, buffer->size); |
203 | if (retval == buffer->size) { | |
064af421 BP |
204 | ofpbuf_delete(buffer); |
205 | return 0; | |
fe55ad15 | 206 | } else if (retval >= 0 || retval == -EAGAIN) { |
064af421 BP |
207 | s->txbuf = buffer; |
208 | if (retval > 0) { | |
209 | ofpbuf_pull(buffer, retval); | |
210 | } | |
064af421 BP |
211 | return 0; |
212 | } else { | |
fe55ad15 | 213 | return -retval; |
064af421 BP |
214 | } |
215 | } | |
216 | ||
60cb3eb8 | 217 | static void |
fe55ad15 | 218 | vconn_stream_run(struct vconn *vconn) |
60cb3eb8 | 219 | { |
fe55ad15 BP |
220 | struct vconn_stream *s = vconn_stream_cast(vconn); |
221 | ssize_t retval; | |
60cb3eb8 | 222 | |
12d68589 | 223 | stream_run(s->stream); |
60cb3eb8 BP |
224 | if (!s->txbuf) { |
225 | return; | |
226 | } | |
227 | ||
6fd6ed71 | 228 | retval = stream_send(s->stream, s->txbuf->data, s->txbuf->size); |
fe55ad15 BP |
229 | if (retval < 0) { |
230 | if (retval != -EAGAIN) { | |
10a89ef0 | 231 | VLOG_ERR_RL(&rl, "send: %s", ovs_strerror(-retval)); |
fe55ad15 | 232 | vconn_stream_clear_txbuf(s); |
60cb3eb8 BP |
233 | return; |
234 | } | |
fe55ad15 BP |
235 | } else if (retval > 0) { |
236 | ofpbuf_pull(s->txbuf, retval); | |
6fd6ed71 | 237 | if (!s->txbuf->size) { |
fe55ad15 | 238 | vconn_stream_clear_txbuf(s); |
60cb3eb8 BP |
239 | return; |
240 | } | |
241 | } | |
242 | } | |
243 | ||
244 | static void | |
fe55ad15 | 245 | vconn_stream_run_wait(struct vconn *vconn) |
60cb3eb8 | 246 | { |
fe55ad15 | 247 | struct vconn_stream *s = vconn_stream_cast(vconn); |
60cb3eb8 | 248 | |
12d68589 | 249 | stream_run_wait(s->stream); |
60cb3eb8 | 250 | if (s->txbuf) { |
fe55ad15 | 251 | stream_send_wait(s->stream); |
60cb3eb8 BP |
252 | } |
253 | } | |
254 | ||
064af421 | 255 | static void |
fe55ad15 | 256 | vconn_stream_wait(struct vconn *vconn, enum vconn_wait_type wait) |
064af421 | 257 | { |
fe55ad15 | 258 | struct vconn_stream *s = vconn_stream_cast(vconn); |
064af421 BP |
259 | switch (wait) { |
260 | case WAIT_CONNECT: | |
fe55ad15 | 261 | stream_connect_wait(s->stream); |
064af421 BP |
262 | break; |
263 | ||
264 | case WAIT_SEND: | |
265 | if (!s->txbuf) { | |
fe55ad15 | 266 | stream_send_wait(s->stream); |
064af421 | 267 | } else { |
fe55ad15 BP |
268 | /* Nothing to do: need to drain txbuf first. |
269 | * vconn_stream_run_wait() will arrange to wake up when there room | |
270 | * to send data, so there's no point in calling poll_fd_wait() | |
271 | * redundantly here. */ | |
064af421 BP |
272 | } |
273 | break; | |
274 | ||
275 | case WAIT_RECV: | |
fe55ad15 | 276 | stream_recv_wait(s->stream); |
064af421 BP |
277 | break; |
278 | ||
279 | default: | |
428b2edd | 280 | OVS_NOT_REACHED(); |
064af421 BP |
281 | } |
282 | } | |
064af421 BP |
283 | \f |
284 | /* Passive stream socket vconn. */ | |
285 | ||
fe55ad15 | 286 | struct pvconn_pstream |
064af421 BP |
287 | { |
288 | struct pvconn pvconn; | |
fe55ad15 | 289 | struct pstream *pstream; |
064af421 BP |
290 | }; |
291 | ||
5b712636 | 292 | static const struct pvconn_class pstream_pvconn_class; |
064af421 | 293 | |
fe55ad15 BP |
294 | static struct pvconn_pstream * |
295 | pvconn_pstream_cast(struct pvconn *pvconn) | |
064af421 | 296 | { |
fe55ad15 | 297 | return CONTAINER_OF(pvconn, struct pvconn_pstream, pvconn); |
064af421 BP |
298 | } |
299 | ||
fe55ad15 BP |
300 | /* Creates a new pvconn named 'name' that will accept new connections using |
301 | * pstream_accept() and stores a pointer to the pvconn in '*pvconnp'. | |
56192221 BP |
302 | * |
303 | * Returns 0 if successful, otherwise a positive errno value. (The current | |
304 | * implementation never fails.) */ | |
fe55ad15 | 305 | static int |
7a25bd99 SH |
306 | pvconn_pstream_listen(const char *name, uint32_t allowed_versions, |
307 | char *suffix OVS_UNUSED, struct pvconn **pvconnp, | |
308 | uint8_t dscp) | |
064af421 | 309 | { |
fe55ad15 BP |
310 | struct pvconn_pstream *ps; |
311 | struct pstream *pstream; | |
fe55ad15 BP |
312 | int error; |
313 | ||
d4763d1d | 314 | error = pstream_open_with_default_port(name, OFP_PORT, &pstream, dscp); |
fe55ad15 BP |
315 | if (error) { |
316 | return error; | |
317 | } | |
318 | ||
319 | ps = xmalloc(sizeof *ps); | |
7a25bd99 | 320 | pvconn_init(&ps->pvconn, &pstream_pvconn_class, name, allowed_versions); |
fe55ad15 | 321 | ps->pstream = pstream; |
064af421 BP |
322 | *pvconnp = &ps->pvconn; |
323 | return 0; | |
324 | } | |
325 | ||
326 | static void | |
fe55ad15 | 327 | pvconn_pstream_close(struct pvconn *pvconn) |
064af421 | 328 | { |
fe55ad15 BP |
329 | struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); |
330 | pstream_close(ps->pstream); | |
064af421 BP |
331 | free(ps); |
332 | } | |
333 | ||
334 | static int | |
fe55ad15 | 335 | pvconn_pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp) |
064af421 | 336 | { |
fe55ad15 BP |
337 | struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); |
338 | struct stream *stream; | |
339 | int error; | |
340 | ||
341 | error = pstream_accept(ps->pstream, &stream); | |
342 | if (error) { | |
343 | if (error != EAGAIN) { | |
344 | VLOG_DBG_RL(&rl, "%s: accept: %s", | |
10a89ef0 | 345 | pstream_get_name(ps->pstream), ovs_strerror(error)); |
064af421 | 346 | } |
fe55ad15 | 347 | return error; |
064af421 BP |
348 | } |
349 | ||
7a25bd99 | 350 | *new_vconnp = vconn_stream_new(stream, 0, pvconn->allowed_versions); |
fe55ad15 | 351 | return 0; |
064af421 BP |
352 | } |
353 | ||
354 | static void | |
fe55ad15 | 355 | pvconn_pstream_wait(struct pvconn *pvconn) |
064af421 | 356 | { |
fe55ad15 BP |
357 | struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); |
358 | pstream_wait(ps->pstream); | |
064af421 | 359 | } |
56192221 | 360 | \f |
fe55ad15 BP |
361 | /* Stream-based vconns and pvconns. */ |
362 | ||
740bc6a4 BP |
363 | #define STREAM_INIT(NAME) \ |
364 | { \ | |
365 | NAME, \ | |
fe55ad15 BP |
366 | vconn_stream_open, \ |
367 | vconn_stream_close, \ | |
368 | vconn_stream_connect, \ | |
369 | vconn_stream_recv, \ | |
370 | vconn_stream_send, \ | |
371 | vconn_stream_run, \ | |
372 | vconn_stream_run_wait, \ | |
373 | vconn_stream_wait, \ | |
740bc6a4 | 374 | } |
fe55ad15 | 375 | |
740bc6a4 BP |
376 | #define PSTREAM_INIT(NAME) \ |
377 | { \ | |
378 | NAME, \ | |
fe55ad15 BP |
379 | pvconn_pstream_listen, \ |
380 | pvconn_pstream_close, \ | |
381 | pvconn_pstream_accept, \ | |
382 | pvconn_pstream_wait \ | |
740bc6a4 | 383 | } |
fe55ad15 | 384 | |
5b712636 BP |
385 | static const struct vconn_class stream_vconn_class = STREAM_INIT("stream"); |
386 | static const struct pvconn_class pstream_pvconn_class = PSTREAM_INIT("pstream"); | |
fe55ad15 | 387 | |
5b712636 BP |
388 | const struct vconn_class tcp_vconn_class = STREAM_INIT("tcp"); |
389 | const struct pvconn_class ptcp_pvconn_class = PSTREAM_INIT("ptcp"); | |
fe55ad15 | 390 | |
5b712636 BP |
391 | const struct vconn_class unix_vconn_class = STREAM_INIT("unix"); |
392 | const struct pvconn_class punix_pvconn_class = PSTREAM_INIT("punix"); | |
fe55ad15 BP |
393 | |
394 | #ifdef HAVE_OPENSSL | |
5b712636 BP |
395 | const struct vconn_class ssl_vconn_class = STREAM_INIT("ssl"); |
396 | const struct pvconn_class pssl_pvconn_class = PSTREAM_INIT("pssl"); | |
fe55ad15 | 397 | #endif |