1 |
|
|
/* $OpenBSD: ofp10.c,v 1.19 2016/12/02 14:39:46 rzalamena Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <sys/types.h> |
20 |
|
|
#include <sys/queue.h> |
21 |
|
|
#include <sys/socket.h> |
22 |
|
|
|
23 |
|
|
#include <net/if.h> |
24 |
|
|
#include <net/if_arp.h> |
25 |
|
|
#include <net/ofp.h> |
26 |
|
|
|
27 |
|
|
#include <netinet/in.h> |
28 |
|
|
#include <netinet/if_ether.h> |
29 |
|
|
#include <netinet/tcp.h> |
30 |
|
|
|
31 |
|
|
#include <stdio.h> |
32 |
|
|
#include <stdlib.h> |
33 |
|
|
#include <unistd.h> |
34 |
|
|
#include <string.h> |
35 |
|
|
#include <fcntl.h> |
36 |
|
|
#include <imsg.h> |
37 |
|
|
#include <event.h> |
38 |
|
|
|
39 |
|
|
#include "ofp10.h" |
40 |
|
|
#include "switchd.h" |
41 |
|
|
#include "ofp_map.h" |
42 |
|
|
|
43 |
|
|
|
44 |
|
|
int ofp10_packet_match(struct packet *, struct ofp10_match *, unsigned int); |
45 |
|
|
|
46 |
|
|
int ofp10_features_reply(struct switchd *, struct switch_connection *, |
47 |
|
|
struct ofp_header *, struct ibuf *); |
48 |
|
|
int ofp10_validate_features_reply(struct switchd *, |
49 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
50 |
|
|
struct ofp_header *, struct ibuf *); |
51 |
|
|
int ofp10_echo_request(struct switchd *, struct switch_connection *, |
52 |
|
|
struct ofp_header *, struct ibuf *); |
53 |
|
|
int ofp10_validate_error(struct switchd *, |
54 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
55 |
|
|
struct ofp_header *, struct ibuf *); |
56 |
|
|
int ofp10_error(struct switchd *, struct switch_connection *, |
57 |
|
|
struct ofp_header *, struct ibuf *); |
58 |
|
|
int ofp10_validate_packet_in(struct switchd *, |
59 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
60 |
|
|
struct ofp_header *, struct ibuf *); |
61 |
|
|
int ofp10_packet_in(struct switchd *, struct switch_connection *, |
62 |
|
|
struct ofp_header *, struct ibuf *); |
63 |
|
|
int ofp10_validate_packet_out(struct switchd *, |
64 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
65 |
|
|
struct ofp_header *, struct ibuf *); |
66 |
|
|
|
67 |
|
|
struct ofp_callback ofp10_callbacks[] = { |
68 |
|
|
{ OFP10_T_HELLO, ofp10_hello, ofp_validate_hello }, |
69 |
|
|
{ OFP10_T_ERROR, NULL, ofp10_validate_error }, |
70 |
|
|
{ OFP10_T_ECHO_REQUEST, ofp10_echo_request, NULL }, |
71 |
|
|
{ OFP10_T_ECHO_REPLY, NULL, NULL }, |
72 |
|
|
{ OFP10_T_EXPERIMENTER, NULL, NULL }, |
73 |
|
|
{ OFP10_T_FEATURES_REQUEST, NULL, NULL }, |
74 |
|
|
{ OFP10_T_FEATURES_REPLY, ofp10_features_reply, |
75 |
|
|
ofp10_validate_features_reply }, |
76 |
|
|
{ OFP10_T_GET_CONFIG_REQUEST, NULL, NULL }, |
77 |
|
|
{ OFP10_T_GET_CONFIG_REPLY, NULL, NULL }, |
78 |
|
|
{ OFP10_T_SET_CONFIG, NULL, NULL }, |
79 |
|
|
{ OFP10_T_PACKET_IN, ofp10_packet_in, ofp10_validate_packet_in }, |
80 |
|
|
{ OFP10_T_FLOW_REMOVED, NULL, NULL }, |
81 |
|
|
{ OFP10_T_PORT_STATUS, NULL, NULL }, |
82 |
|
|
{ OFP10_T_PACKET_OUT, NULL, ofp10_validate_packet_out }, |
83 |
|
|
{ OFP10_T_FLOW_MOD, NULL, NULL }, |
84 |
|
|
{ OFP10_T_PORT_MOD, NULL, NULL }, |
85 |
|
|
{ OFP10_T_STATS_REQUEST, NULL, NULL }, |
86 |
|
|
{ OFP10_T_STATS_REPLY, NULL, NULL }, |
87 |
|
|
{ OFP10_T_BARRIER_REQUEST, NULL, NULL }, |
88 |
|
|
{ OFP10_T_BARRIER_REPLY, NULL, NULL }, |
89 |
|
|
{ OFP10_T_QUEUE_GET_CONFIG_REQUEST, NULL, NULL }, |
90 |
|
|
{ OFP10_T_QUEUE_GET_CONFIG_REPLY, NULL, NULL } |
91 |
|
|
}; |
92 |
|
|
|
93 |
|
|
int |
94 |
|
|
ofp10_validate(struct switchd *sc, |
95 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
96 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
97 |
|
|
{ |
98 |
|
|
uint8_t type; |
99 |
|
|
|
100 |
|
|
if (ofp_validate_header(sc, src, dst, oh, OFP_V_1_0) != 0) { |
101 |
|
|
log_debug("\tinvalid header"); |
102 |
|
|
return (-1); |
103 |
|
|
} |
104 |
|
|
if (ibuf == NULL) { |
105 |
|
|
/* The response packet buffer is optional */ |
106 |
|
|
return (0); |
107 |
|
|
} |
108 |
|
|
type = oh->oh_type; |
109 |
|
|
if (ofp10_callbacks[type].validate != NULL && |
110 |
|
|
ofp10_callbacks[type].validate(sc, src, dst, oh, ibuf) != 0) { |
111 |
|
|
log_debug("\tinvalid packet"); |
112 |
|
|
return (-1); |
113 |
|
|
} |
114 |
|
|
return (0); |
115 |
|
|
} |
116 |
|
|
|
117 |
|
|
int |
118 |
|
|
ofp10_validate_packet_in(struct switchd *sc, |
119 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
120 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
121 |
|
|
{ |
122 |
|
|
struct ofp10_packet_in *pin; |
123 |
|
|
uint8_t *p; |
124 |
|
|
size_t len, plen; |
125 |
|
|
off_t off; |
126 |
|
|
|
127 |
|
|
off = 0; |
128 |
|
|
if ((pin = ibuf_seek(ibuf, off, sizeof(*pin))) == NULL) |
129 |
|
|
return (-1); |
130 |
|
|
log_debug("\tbuffer %d port %s " |
131 |
|
|
"length %u reason %u", |
132 |
|
|
ntohl(pin->pin_buffer_id), |
133 |
|
|
print_map(ntohs(pin->pin_port), ofp10_port_map), |
134 |
|
|
ntohs(pin->pin_total_len), |
135 |
|
|
pin->pin_reason); |
136 |
|
|
off += sizeof(*pin); |
137 |
|
|
|
138 |
|
|
len = ntohs(pin->pin_total_len); |
139 |
|
|
plen = ibuf_length(ibuf) - off; |
140 |
|
|
|
141 |
|
|
if (plen < len) { |
142 |
|
|
log_debug("\ttruncated packet %zu < %zu", plen, len); |
143 |
|
|
|
144 |
|
|
/* Buffered packets can be truncated */ |
145 |
|
|
if (pin->pin_buffer_id != OFP_PKTOUT_NO_BUFFER) |
146 |
|
|
len = plen; |
147 |
|
|
else |
148 |
|
|
return (-1); |
149 |
|
|
} |
150 |
|
|
if ((p = ibuf_seek(ibuf, off, len)) == NULL) |
151 |
|
|
return (-1); |
152 |
|
|
if (sc->sc_tap != -1) |
153 |
|
|
(void)write(sc->sc_tap, p, len); |
154 |
|
|
|
155 |
|
|
return (0); |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
int |
159 |
|
|
ofp10_validate_packet_out(struct switchd *sc, |
160 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
161 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
162 |
|
|
{ |
163 |
|
|
struct ofp10_packet_out *pout; |
164 |
|
|
size_t len; |
165 |
|
|
off_t off; |
166 |
|
|
struct ofp_action_header *ah; |
167 |
|
|
struct ofp10_action_output *ao; |
168 |
|
|
|
169 |
|
|
off = 0; |
170 |
|
|
if ((pout = ibuf_seek(ibuf, off, sizeof(*pout))) == NULL) { |
171 |
|
|
log_debug("%s: seek failed: length %zd", |
172 |
|
|
__func__, ibuf_length(ibuf)); |
173 |
|
|
return (-1); |
174 |
|
|
} |
175 |
|
|
log_debug("\tbuffer %d port %s " |
176 |
|
|
"actions length %u", |
177 |
|
|
ntohl(pout->pout_buffer_id), |
178 |
|
|
print_map(ntohs(pout->pout_port), ofp10_port_map), |
179 |
|
|
ntohs(pout->pout_actions_len)); |
180 |
|
|
len = ntohs(pout->pout_actions_len); |
181 |
|
|
|
182 |
|
|
off += sizeof(*pout); |
183 |
|
|
while ((ah = ibuf_seek(ibuf, off, len)) != NULL && |
184 |
|
|
ntohs(ah->ah_len) >= (uint16_t)sizeof(*ah)) { |
185 |
|
|
switch (ntohs(ah->ah_type)) { |
186 |
|
|
case OFP10_ACTION_OUTPUT: |
187 |
|
|
ao = (struct ofp10_action_output *)ah; |
188 |
|
|
log_debug("\t\taction type %s length %d " |
189 |
|
|
"port %s max length %d", |
190 |
|
|
print_map(ntohs(ao->ao_type), ofp10_action_map), |
191 |
|
|
ntohs(ao->ao_len), |
192 |
|
|
print_map(ntohs(ao->ao_port), ofp10_port_map), |
193 |
|
|
ntohs(ao->ao_max_len)); |
194 |
|
|
break; |
195 |
|
|
default: |
196 |
|
|
log_debug("\t\taction type %s length %d", |
197 |
|
|
print_map(ntohs(ah->ah_type), ofp10_action_map), |
198 |
|
|
ntohs(ah->ah_len)); |
199 |
|
|
break; |
200 |
|
|
} |
201 |
|
|
if (pout->pout_buffer_id == (uint32_t)-1) |
202 |
|
|
break; |
203 |
|
|
off += ntohs(ah->ah_len); |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
return (0); |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
int |
210 |
|
|
ofp10_validate_error(struct switchd *sc, |
211 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
212 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
213 |
|
|
{ |
214 |
|
|
struct ofp_error *err; |
215 |
|
|
off_t off; |
216 |
|
|
const char *code; |
217 |
|
|
|
218 |
|
|
off = 0; |
219 |
|
|
if ((err = ibuf_seek(ibuf, off, sizeof(*err))) == NULL) { |
220 |
|
|
log_debug("%s: seek failed: length %zd", |
221 |
|
|
__func__, ibuf_length(ibuf)); |
222 |
|
|
return (-1); |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
switch (ntohs(err->err_type)) { |
226 |
|
|
case OFP10_ERRTYPE_FLOW_MOD_FAILED: |
227 |
|
|
code = print_map(ntohs(err->err_code), ofp10_errflowmod_map); |
228 |
|
|
break; |
229 |
|
|
default: |
230 |
|
|
code = NULL; |
231 |
|
|
break; |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
log_debug("\terror type %s code %u%s%s", |
235 |
|
|
print_map(ntohs(err->err_type), ofp10_errtype_map), |
236 |
|
|
ntohs(err->err_code), |
237 |
|
|
code == NULL ? "" : ": ", |
238 |
|
|
code == NULL ? "" : code); |
239 |
|
|
|
240 |
|
|
return (0); |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
int |
244 |
|
|
ofp10_input(struct switchd *sc, struct switch_connection *con, |
245 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
246 |
|
|
{ |
247 |
|
|
if (ofp10_validate(sc, &con->con_peer, &con->con_local, oh, ibuf) != 0) |
248 |
|
|
return (-1); |
249 |
|
|
|
250 |
|
|
if (ofp10_callbacks[oh->oh_type].cb == NULL) { |
251 |
|
|
log_debug("message not supported: %s", |
252 |
|
|
print_map(oh->oh_type, ofp10_t_map)); |
253 |
|
|
return (-1); |
254 |
|
|
} |
255 |
|
|
if (ofp10_callbacks[oh->oh_type].cb(sc, con, oh, ibuf) != 0) |
256 |
|
|
return (-1); |
257 |
|
|
|
258 |
|
|
return (0); |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
int |
262 |
|
|
ofp10_hello(struct switchd *sc, struct switch_connection *con, |
263 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
264 |
|
|
{ |
265 |
|
|
if (switch_add(con) == NULL) { |
266 |
|
|
log_debug("%s: failed to add switch", __func__); |
267 |
|
|
return (-1); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
if (ofp_recv_hello(sc, con, oh, ibuf) == -1) |
271 |
|
|
return (-1); |
272 |
|
|
|
273 |
|
|
return (ofp_nextstate(sc, con, OFP_STATE_FEATURE_WAIT)); |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
int |
277 |
|
|
ofp10_features_reply(struct switchd *sc, struct switch_connection *con, |
278 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
279 |
|
|
{ |
280 |
|
|
return (ofp_nextstate(sc, con, OFP_STATE_ESTABLISHED)); |
281 |
|
|
} |
282 |
|
|
|
283 |
|
|
int |
284 |
|
|
ofp10_validate_features_reply(struct switchd *sc, |
285 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
286 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
287 |
|
|
{ |
288 |
|
|
struct ofp_switch_features *swf; |
289 |
|
|
struct ofp10_phy_port *swp; |
290 |
|
|
off_t poff; |
291 |
|
|
int portslen; |
292 |
|
|
char *mac; |
293 |
|
|
|
294 |
|
|
if ((swf = ibuf_seek(ibuf, 0, sizeof(*swf))) == NULL) |
295 |
|
|
return (-1); |
296 |
|
|
|
297 |
|
|
log_debug("\tdatapath_id %#016llx nbuffers %u ntables %d " |
298 |
|
|
"capabilities %#08x actions %#08x", |
299 |
|
|
be64toh(swf->swf_datapath_id), ntohl(swf->swf_nbuffers), |
300 |
|
|
swf->swf_ntables, ntohl(swf->swf_capabilities), |
301 |
|
|
ntohl(swf->swf_actions)); |
302 |
|
|
|
303 |
|
|
poff = sizeof(*swf); |
304 |
|
|
portslen = ntohs(oh->oh_length) - sizeof(*swf); |
305 |
|
|
if (portslen <= 0) |
306 |
|
|
return (0); |
307 |
|
|
|
308 |
|
|
while (portslen > 0) { |
309 |
|
|
if ((swp = ibuf_seek(ibuf, poff, sizeof(*swp))) == NULL) |
310 |
|
|
return (-1); |
311 |
|
|
|
312 |
|
|
mac = ether_ntoa((void *)swp->swp_macaddr); |
313 |
|
|
log_debug("no %s macaddr %s name %s config %#08x state %#08x " |
314 |
|
|
"cur %#08x advertised %#08x supported %#08x peer %#08x", |
315 |
|
|
print_map(ntohs(swp->swp_number), ofp10_port_map), mac, |
316 |
|
|
swp->swp_name, swp->swp_config, swp->swp_state, |
317 |
|
|
swp->swp_cur, swp->swp_advertised, swp->swp_supported, |
318 |
|
|
swp->swp_peer); |
319 |
|
|
|
320 |
|
|
portslen -= sizeof(*swp); |
321 |
|
|
poff += sizeof(*swp); |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
return (0); |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
int |
328 |
|
|
ofp10_echo_request(struct switchd *sc, struct switch_connection *con, |
329 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
330 |
|
|
{ |
331 |
|
|
/* Echo reply */ |
332 |
|
|
oh->oh_type = OFP10_T_ECHO_REPLY; |
333 |
|
|
if (ofp10_validate(sc, &con->con_local, &con->con_peer, oh, NULL) != 0) |
334 |
|
|
return (-1); |
335 |
|
|
ofp_output(con, oh, NULL); |
336 |
|
|
|
337 |
|
|
return (0); |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
int |
341 |
|
|
ofp10_packet_match(struct packet *pkt, struct ofp10_match *m, uint32_t flags) |
342 |
|
|
{ |
343 |
|
|
struct ether_header *eh = pkt->pkt_eh; |
344 |
|
|
|
345 |
|
|
bzero(m, sizeof(*m)); |
346 |
|
|
m->m_wildcards = htonl(~flags); |
347 |
|
|
|
348 |
|
|
if ((flags & (OFP10_WILDCARD_DL_SRC|OFP10_WILDCARD_DL_DST)) && |
349 |
|
|
(eh == NULL)) |
350 |
|
|
return (-1); |
351 |
|
|
|
352 |
|
|
if (flags & OFP10_WILDCARD_DL_SRC) |
353 |
|
|
memcpy(m->m_dl_src, eh->ether_shost, ETHER_ADDR_LEN); |
354 |
|
|
if (flags & OFP10_WILDCARD_DL_DST) |
355 |
|
|
memcpy(m->m_dl_dst, eh->ether_dhost, ETHER_ADDR_LEN); |
356 |
|
|
|
357 |
|
|
return (0); |
358 |
|
|
} |
359 |
|
|
|
360 |
|
|
int |
361 |
|
|
ofp10_packet_in(struct switchd *sc, struct switch_connection *con, |
362 |
|
|
struct ofp_header *ih, struct ibuf *ibuf) |
363 |
|
|
{ |
364 |
|
|
struct ofp10_packet_in *pin; |
365 |
|
|
struct ofp10_packet_out *pout; |
366 |
|
|
struct ofp10_action_output *ao; |
367 |
|
|
struct ofp10_flow_mod *fm; |
368 |
|
|
struct ofp_header *oh; |
369 |
|
|
struct packet pkt; |
370 |
|
|
struct ibuf *obuf = NULL; |
371 |
|
|
int ret = -1; |
372 |
|
|
size_t len; |
373 |
|
|
uint32_t srcport, dstport; |
374 |
|
|
int addflow = 0; |
375 |
|
|
int addpacket = 0; |
376 |
|
|
|
377 |
|
|
if ((pin = ibuf_getdata(ibuf, sizeof(*pin))) == NULL) |
378 |
|
|
return (-1); |
379 |
|
|
|
380 |
|
|
bzero(&pkt, sizeof(pkt)); |
381 |
|
|
len = ntohs(pin->pin_total_len); |
382 |
|
|
srcport = ntohs(pin->pin_port); |
383 |
|
|
|
384 |
|
|
if (packet_input(sc, con->con_switch, |
385 |
|
|
srcport, &dstport, ibuf, len, &pkt) == -1 || |
386 |
|
|
(dstport > OFP10_PORT_MAX && |
387 |
|
|
dstport != OFP10_PORT_LOCAL && |
388 |
|
|
dstport != OFP10_PORT_CONTROLLER)) { |
389 |
|
|
/* fallback to flooding */ |
390 |
|
|
dstport = OFP10_PORT_FLOOD; |
391 |
|
|
} else if (srcport == dstport) { |
392 |
|
|
/* |
393 |
|
|
* silently drop looping packet |
394 |
|
|
* (don't use OFP10_PORT_INPUT here) |
395 |
|
|
*/ |
396 |
|
|
dstport = OFP10_PORT_ANY; |
397 |
|
|
} else { |
398 |
|
|
addflow = 1; |
399 |
|
|
} |
400 |
|
|
|
401 |
|
|
if ((obuf = ibuf_static()) == NULL) |
402 |
|
|
goto done; |
403 |
|
|
|
404 |
|
|
again: |
405 |
|
|
if (addflow) { |
406 |
|
|
if ((fm = ibuf_advance(obuf, sizeof(*fm))) == NULL) |
407 |
|
|
goto done; |
408 |
|
|
|
409 |
|
|
ofp10_packet_match(&pkt, &fm->fm_match, OFP10_WILDCARD_DL_DST); |
410 |
|
|
|
411 |
|
|
oh = &fm->fm_oh; |
412 |
|
|
fm->fm_cookie = 0; /* XXX should we set a cookie? */ |
413 |
|
|
fm->fm_command = htons(OFP_FLOWCMD_ADD); |
414 |
|
|
fm->fm_idle_timeout = htons(sc->sc_cache_timeout); |
415 |
|
|
fm->fm_hard_timeout = 0; /* permanent */ |
416 |
|
|
fm->fm_priority = 0; |
417 |
|
|
fm->fm_buffer_id = pin->pin_buffer_id; |
418 |
|
|
fm->fm_flags = htons(OFP_FLOWFLAG_SEND_FLOW_REMOVED); |
419 |
|
|
if (pin->pin_buffer_id == htonl(OFP_PKTOUT_NO_BUFFER)) |
420 |
|
|
addpacket = 1; |
421 |
|
|
} else { |
422 |
|
|
if ((pout = ibuf_advance(obuf, sizeof(*pout))) == NULL) |
423 |
|
|
goto done; |
424 |
|
|
|
425 |
|
|
oh = &pout->pout_oh; |
426 |
|
|
pout->pout_buffer_id = pin->pin_buffer_id; |
427 |
|
|
pout->pout_port = pin->pin_port; |
428 |
|
|
pout->pout_actions_len = htons(sizeof(*ao)); |
429 |
|
|
|
430 |
|
|
if (pin->pin_buffer_id == htonl(OFP_PKTOUT_NO_BUFFER)) |
431 |
|
|
addpacket = 1; |
432 |
|
|
} |
433 |
|
|
|
434 |
|
|
if ((ao = ibuf_advance(obuf, sizeof(*ao))) == NULL) |
435 |
|
|
goto done; |
436 |
|
|
ao->ao_type = htons(OFP_ACTION_OUTPUT); |
437 |
|
|
ao->ao_len = htons(sizeof(*ao)); |
438 |
|
|
ao->ao_port = htons((uint16_t)dstport); |
439 |
|
|
ao->ao_max_len = 0; |
440 |
|
|
|
441 |
|
|
/* Add optional packet payload to packet-out. */ |
442 |
|
|
if (addflow == 0 && addpacket && |
443 |
|
|
imsg_add(obuf, pkt.pkt_buf, pkt.pkt_len) == -1) |
444 |
|
|
goto done; |
445 |
|
|
|
446 |
|
|
/* Set output header */ |
447 |
|
|
memcpy(oh, ih, sizeof(*oh)); |
448 |
|
|
oh->oh_length = htons(ibuf_length(obuf)); |
449 |
|
|
oh->oh_type = addflow ? OFP10_T_FLOW_MOD : OFP10_T_PACKET_OUT; |
450 |
|
|
oh->oh_xid = htonl(con->con_xidnxt++); |
451 |
|
|
|
452 |
|
|
if (ofp10_validate(sc, &con->con_local, &con->con_peer, oh, obuf) != 0) |
453 |
|
|
goto done; |
454 |
|
|
|
455 |
|
|
ofp_output(con, NULL, obuf); |
456 |
|
|
|
457 |
|
|
if (addflow && addpacket) { |
458 |
|
|
/* loop to output the packet again */ |
459 |
|
|
addflow = 0; |
460 |
|
|
if ((obuf = ibuf_static()) == NULL) |
461 |
|
|
goto done; |
462 |
|
|
goto again; |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
ret = 0; |
466 |
|
|
done: |
467 |
|
|
ibuf_release(obuf); |
468 |
|
|
return (ret); |
469 |
|
|
} |