1 |
|
|
/* $OpenBSD: ofp13.c,v 1.43 2017/01/17 09:21:50 rzalamena Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> |
5 |
|
|
* Copyright (c) 2016 Rafael Zalamena <rzalamena@openbsd.org> |
6 |
|
|
* |
7 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
8 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
9 |
|
|
* copyright notice and this permission notice appear in all copies. |
10 |
|
|
* |
11 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 |
|
|
*/ |
19 |
|
|
|
20 |
|
|
#include <sys/types.h> |
21 |
|
|
#include <sys/queue.h> |
22 |
|
|
#include <sys/socket.h> |
23 |
|
|
|
24 |
|
|
#include <net/if.h> |
25 |
|
|
#include <net/if_arp.h> |
26 |
|
|
#include <net/ofp.h> |
27 |
|
|
|
28 |
|
|
#include <netinet/in.h> |
29 |
|
|
#include <netinet/if_ether.h> |
30 |
|
|
#include <netinet/tcp.h> |
31 |
|
|
#include <netmpls/mpls.h> |
32 |
|
|
|
33 |
|
|
#include <endian.h> |
34 |
|
|
#include <stdio.h> |
35 |
|
|
#include <stdlib.h> |
36 |
|
|
#include <stddef.h> |
37 |
|
|
#include <unistd.h> |
38 |
|
|
#include <string.h> |
39 |
|
|
#include <fcntl.h> |
40 |
|
|
#include <imsg.h> |
41 |
|
|
#include <event.h> |
42 |
|
|
|
43 |
|
|
#include "switchd.h" |
44 |
|
|
#include "ofp_map.h" |
45 |
|
|
|
46 |
|
|
int ofp13_echo_request(struct switchd *, struct switch_connection *, |
47 |
|
|
struct ofp_header *, struct ibuf *); |
48 |
|
|
int ofp13_validate_features_reply(struct switchd *, |
49 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
50 |
|
|
struct ofp_header *, struct ibuf *); |
51 |
|
|
int ofp13_features_reply(struct switchd *, struct switch_connection *, |
52 |
|
|
struct ofp_header *, struct ibuf *); |
53 |
|
|
int ofp13_validate_error(struct switchd *, |
54 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
55 |
|
|
struct ofp_header *, struct ibuf *); |
56 |
|
|
int ofp13_validate_action(struct switchd *, struct ofp_header *, |
57 |
|
|
struct ibuf *, off_t *, struct ofp_action_header *); |
58 |
|
|
int ofp13_validate_instruction(struct switchd *, struct ofp_header *, |
59 |
|
|
struct ibuf *, off_t *, struct ofp_instruction *); |
60 |
|
|
int ofp13_validate_flow_mod(struct switchd *, struct sockaddr_storage *, |
61 |
|
|
struct sockaddr_storage *, struct ofp_header *, struct ibuf *); |
62 |
|
|
int ofp13_validate_oxm_basic(struct ibuf *, off_t, int, uint8_t); |
63 |
|
|
int ofp13_validate_oxm(struct switchd *, struct ofp_ox_match *, |
64 |
|
|
struct ofp_header *, struct ibuf *, off_t); |
65 |
|
|
int ofp13_validate_packet_in(struct switchd *, |
66 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
67 |
|
|
struct ofp_header *, struct ibuf *); |
68 |
|
|
int ofp13_packet_match(struct ibuf *, struct packet *, struct ofp_match *); |
69 |
|
|
int ofp13_packet_in(struct switchd *, struct switch_connection *, |
70 |
|
|
struct ofp_header *, struct ibuf *); |
71 |
|
|
int ofp13_flow_removed(struct switchd *, struct switch_connection *, |
72 |
|
|
struct ofp_header *, struct ibuf *); |
73 |
|
|
int ofp13_tableproperties(struct switch_connection *, struct ibuf *, |
74 |
|
|
off_t, size_t, int); |
75 |
|
|
int ofp13_multipart_reply(struct switchd *, struct switch_connection *, |
76 |
|
|
struct ofp_header *, struct ibuf *); |
77 |
|
|
int ofp13_validate_tableproperty(struct ibuf *, off_t, int); |
78 |
|
|
int ofp13_multipart_reply_validate(struct switchd *, |
79 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
80 |
|
|
struct ofp_header *, struct ibuf *); |
81 |
|
|
int ofp13_validate_packet_out(struct switchd *, |
82 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
83 |
|
|
struct ofp_header *, struct ibuf *); |
84 |
|
|
|
85 |
|
|
struct ofp_multipart * |
86 |
|
|
ofp13_multipart_request(struct switch_connection *, struct ibuf *, |
87 |
|
|
uint16_t, uint16_t); |
88 |
|
|
int ofp13_multipart_request_validate(struct switchd *, |
89 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
90 |
|
|
struct ofp_header *, struct ibuf *); |
91 |
|
|
|
92 |
|
|
int ofp13_error(struct switchd *, struct switch_connection *, |
93 |
|
|
struct ofp_header *, struct ibuf *, uint16_t, uint16_t); |
94 |
|
|
|
95 |
|
|
struct ofp_group_mod * |
96 |
|
|
ofp13_group(struct switch_connection *, struct ibuf *, |
97 |
|
|
uint32_t, uint16_t, uint8_t); |
98 |
|
|
struct ofp_bucket * |
99 |
|
|
ofp13_bucket(struct ibuf *, uint16_t, uint32_t, uint32_t); |
100 |
|
|
|
101 |
|
|
int ofp13_setconfig_validate(struct switchd *, |
102 |
|
|
struct sockaddr_storage *, struct sockaddr_storage *, |
103 |
|
|
struct ofp_header *, struct ibuf *); |
104 |
|
|
|
105 |
|
|
int ofp13_switchconfigure(struct switchd *, struct switch_connection *); |
106 |
|
|
int ofp13_getflowtable(struct switch_connection *); |
107 |
|
|
|
108 |
|
|
struct ofp_callback ofp13_callbacks[] = { |
109 |
|
|
{ OFP_T_HELLO, ofp13_hello, ofp_validate_hello }, |
110 |
|
|
{ OFP_T_ERROR, NULL, ofp13_validate_error }, |
111 |
|
|
{ OFP_T_ECHO_REQUEST, ofp13_echo_request, NULL }, |
112 |
|
|
{ OFP_T_ECHO_REPLY, NULL, NULL }, |
113 |
|
|
{ OFP_T_EXPERIMENTER, NULL, NULL }, |
114 |
|
|
{ OFP_T_FEATURES_REQUEST, NULL, NULL }, |
115 |
|
|
{ OFP_T_FEATURES_REPLY, ofp13_features_reply, |
116 |
|
|
ofp13_validate_features_reply }, |
117 |
|
|
{ OFP_T_GET_CONFIG_REQUEST, NULL, NULL }, |
118 |
|
|
{ OFP_T_GET_CONFIG_REPLY, NULL, NULL }, |
119 |
|
|
{ OFP_T_SET_CONFIG, NULL, ofp13_setconfig_validate }, |
120 |
|
|
{ OFP_T_PACKET_IN, ofp13_packet_in, |
121 |
|
|
ofp13_validate_packet_in }, |
122 |
|
|
{ OFP_T_FLOW_REMOVED, ofp13_flow_removed, NULL }, |
123 |
|
|
{ OFP_T_PORT_STATUS, NULL, NULL }, |
124 |
|
|
{ OFP_T_PACKET_OUT, NULL, ofp13_validate_packet_out }, |
125 |
|
|
{ OFP_T_FLOW_MOD, NULL, ofp13_validate_flow_mod }, |
126 |
|
|
{ OFP_T_GROUP_MOD, NULL, NULL }, |
127 |
|
|
{ OFP_T_PORT_MOD, NULL, NULL }, |
128 |
|
|
{ OFP_T_TABLE_MOD, NULL, NULL }, |
129 |
|
|
{ OFP_T_MULTIPART_REQUEST, NULL, |
130 |
|
|
ofp13_multipart_request_validate }, |
131 |
|
|
{ OFP_T_MULTIPART_REPLY, ofp13_multipart_reply, |
132 |
|
|
ofp13_multipart_reply_validate }, |
133 |
|
|
{ OFP_T_BARRIER_REQUEST, NULL, NULL }, |
134 |
|
|
{ OFP_T_BARRIER_REPLY, NULL, NULL }, |
135 |
|
|
{ OFP_T_QUEUE_GET_CONFIG_REQUEST, NULL, NULL }, |
136 |
|
|
{ OFP_T_QUEUE_GET_CONFIG_REPLY, NULL, NULL }, |
137 |
|
|
{ OFP_T_ROLE_REQUEST, NULL, NULL }, |
138 |
|
|
{ OFP_T_ROLE_REPLY, NULL, NULL }, |
139 |
|
|
{ OFP_T_GET_ASYNC_REQUEST, NULL, NULL }, |
140 |
|
|
{ OFP_T_GET_ASYNC_REPLY, NULL, NULL }, |
141 |
|
|
{ OFP_T_SET_ASYNC, NULL, NULL }, |
142 |
|
|
{ OFP_T_METER_MOD, NULL, NULL }, |
143 |
|
|
}; |
144 |
|
|
|
145 |
|
|
int |
146 |
|
|
ofp13_validate(struct switchd *sc, |
147 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
148 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
149 |
|
|
{ |
150 |
|
|
uint8_t type; |
151 |
|
|
|
152 |
|
|
if (ofp_validate_header(sc, src, dst, oh, OFP_V_1_3) != 0) { |
153 |
|
|
log_debug("\tinvalid header"); |
154 |
|
|
return (-1); |
155 |
|
|
} |
156 |
|
|
if (ibuf == NULL) { |
157 |
|
|
/* The response packet buffer is optional */ |
158 |
|
|
return (0); |
159 |
|
|
} |
160 |
|
|
type = oh->oh_type; |
161 |
|
|
if (ofp13_callbacks[type].validate != NULL && |
162 |
|
|
ofp13_callbacks[type].validate(sc, src, dst, oh, ibuf) != 0) { |
163 |
|
|
log_debug("\tinvalid packet"); |
164 |
|
|
return (-1); |
165 |
|
|
} |
166 |
|
|
return (0); |
167 |
|
|
} |
168 |
|
|
|
169 |
|
|
int |
170 |
|
|
ofp13_validate_oxm_basic(struct ibuf *ibuf, off_t off, int hasmask, |
171 |
|
|
uint8_t type) |
172 |
|
|
{ |
173 |
|
|
uint8_t *ui8; |
174 |
|
|
uint16_t *ui16; |
175 |
|
|
uint32_t *ui32; |
176 |
|
|
uint64_t *ui64; |
177 |
|
|
int i, len; |
178 |
|
|
char hex[8], buf[64], maskbuf[64]; |
179 |
|
|
|
180 |
|
|
switch (type) { |
181 |
|
|
case OFP_XM_T_IN_PORT: |
182 |
|
|
case OFP_XM_T_IN_PHY_PORT: |
183 |
|
|
case OFP_XM_T_MPLS_LABEL: |
184 |
|
|
if (hasmask) |
185 |
|
|
return (-1); |
186 |
|
|
if ((ui32 = ibuf_seek(ibuf, off, sizeof(*ui32))) == NULL) |
187 |
|
|
return (-1); |
188 |
|
|
|
189 |
|
|
log_debug("\t\t%u", ntohl(*ui32)); |
190 |
|
|
break; |
191 |
|
|
|
192 |
|
|
case OFP_XM_T_META: |
193 |
|
|
case OFP_XM_T_TUNNEL_ID: |
194 |
|
|
len = sizeof(*ui64); |
195 |
|
|
if (hasmask) |
196 |
|
|
len *= 2; |
197 |
|
|
|
198 |
|
|
if ((ui64 = ibuf_seek(ibuf, off, len)) == NULL) |
199 |
|
|
return (-1); |
200 |
|
|
|
201 |
|
|
if (hasmask) |
202 |
|
|
log_debug("\t\t%llu mask %#16llx", |
203 |
|
|
be64toh(*ui64), be64toh(*(ui64 + 1))); |
204 |
|
|
else |
205 |
|
|
log_debug("\t\t%llu", be64toh(*ui64)); |
206 |
|
|
break; |
207 |
|
|
|
208 |
|
|
case OFP_XM_T_ARP_SHA: |
209 |
|
|
case OFP_XM_T_ARP_THA: |
210 |
|
|
case OFP_XM_T_IPV6_ND_SLL: |
211 |
|
|
case OFP_XM_T_IPV6_ND_TLL: |
212 |
|
|
if (hasmask) |
213 |
|
|
return (-1); |
214 |
|
|
if ((ui8 = ibuf_seek(ibuf, off, ETHER_ADDR_LEN)) == NULL) |
215 |
|
|
return (-1); |
216 |
|
|
|
217 |
|
|
buf[0] = 0; |
218 |
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) { |
219 |
|
|
snprintf(hex, sizeof(hex), "%02x", *(ui8 + i)); |
220 |
|
|
strlcat(buf, hex, sizeof(buf)); |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
log_debug("\t\t%s", buf); |
224 |
|
|
break; |
225 |
|
|
|
226 |
|
|
case OFP_XM_T_ETH_DST: |
227 |
|
|
case OFP_XM_T_ETH_SRC: |
228 |
|
|
len = ETHER_ADDR_LEN; |
229 |
|
|
if (hasmask) |
230 |
|
|
len *= 2; |
231 |
|
|
|
232 |
|
|
if ((ui8 = ibuf_seek(ibuf, off, len)) == NULL) |
233 |
|
|
return (-1); |
234 |
|
|
|
235 |
|
|
buf[0] = 0; |
236 |
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) { |
237 |
|
|
snprintf(hex, sizeof(hex), "%02x", *(ui8 + i)); |
238 |
|
|
strlcat(buf, hex, sizeof(buf)); |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
if (hasmask) { |
242 |
|
|
maskbuf[0] = 0; |
243 |
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) { |
244 |
|
|
snprintf(hex, sizeof(hex), "%02x", *(ui8 + |
245 |
|
|
(i + ETHER_ADDR_LEN))); |
246 |
|
|
strlcat(maskbuf, hex, sizeof(maskbuf)); |
247 |
|
|
} |
248 |
|
|
log_debug("\t\t%s mask %s", buf, maskbuf); |
249 |
|
|
} else |
250 |
|
|
log_debug("\t\t%s", buf); |
251 |
|
|
break; |
252 |
|
|
|
253 |
|
|
case OFP_XM_T_ETH_TYPE: |
254 |
|
|
if (hasmask) |
255 |
|
|
return (-1); |
256 |
|
|
len = sizeof(*ui16); |
257 |
|
|
if ((ui16 = ibuf_seek(ibuf, off, len)) == NULL) |
258 |
|
|
return (-1); |
259 |
|
|
log_debug("\t\t0x%04x", ntohs(*ui16)); |
260 |
|
|
break; |
261 |
|
|
|
262 |
|
|
case OFP_XM_T_TCP_SRC: |
263 |
|
|
case OFP_XM_T_TCP_DST: |
264 |
|
|
case OFP_XM_T_UDP_SRC: |
265 |
|
|
case OFP_XM_T_UDP_DST: |
266 |
|
|
case OFP_XM_T_SCTP_SRC: |
267 |
|
|
case OFP_XM_T_SCTP_DST: |
268 |
|
|
case OFP_XM_T_ARP_OP: |
269 |
|
|
if (hasmask) |
270 |
|
|
return (-1); |
271 |
|
|
if ((ui16 = ibuf_seek(ibuf, off, sizeof(*ui16))) == NULL) |
272 |
|
|
return (-1); |
273 |
|
|
|
274 |
|
|
log_debug("\t\t%d", ntohs(*ui16)); |
275 |
|
|
break; |
276 |
|
|
|
277 |
|
|
case OFP_XM_T_VLAN_VID: |
278 |
|
|
case OFP_XM_T_IPV6_EXTHDR: |
279 |
|
|
len = sizeof(*ui16); |
280 |
|
|
if (hasmask) |
281 |
|
|
len *= 2; |
282 |
|
|
|
283 |
|
|
if ((ui16 = ibuf_seek(ibuf, off, len)) == NULL) |
284 |
|
|
return (-1); |
285 |
|
|
|
286 |
|
|
if (type == OFP_XM_T_VLAN_VID) { |
287 |
|
|
/* Remove the VID present bit to display. */ |
288 |
|
|
if (hasmask) |
289 |
|
|
log_debug("\t\t%d mask %#04x", |
290 |
|
|
ntohs(*ui16) & ~OFP_XM_VID_PRESENT, |
291 |
|
|
ntohs(*(ui16 + 1))); |
292 |
|
|
else |
293 |
|
|
log_debug("\t\t%d", |
294 |
|
|
ntohs(*ui16) & ~OFP_XM_VID_PRESENT); |
295 |
|
|
break; |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
if (hasmask) |
299 |
|
|
log_debug("\t\t%d mask %#04x", |
300 |
|
|
ntohs(*ui16), ntohs(*(ui16 + 1))); |
301 |
|
|
else |
302 |
|
|
log_debug("\t\t%d", ntohs(*ui16)); |
303 |
|
|
break; |
304 |
|
|
|
305 |
|
|
case OFP_XM_T_IP_DSCP: |
306 |
|
|
case OFP_XM_T_IP_ECN: |
307 |
|
|
case OFP_XM_T_IP_PROTO: |
308 |
|
|
case OFP_XM_T_ICMPV4_TYPE: |
309 |
|
|
case OFP_XM_T_ICMPV4_CODE: |
310 |
|
|
case OFP_XM_T_ICMPV6_TYPE: |
311 |
|
|
case OFP_XM_T_ICMPV6_CODE: |
312 |
|
|
case OFP_XM_T_MPLS_TC: |
313 |
|
|
case OFP_XM_T_MPLS_BOS: |
314 |
|
|
if (hasmask) |
315 |
|
|
return (-1); |
316 |
|
|
if ((ui8 = ibuf_seek(ibuf, off, sizeof(*ui8))) == NULL) |
317 |
|
|
return (-1); |
318 |
|
|
|
319 |
|
|
log_debug("\t\t%#02x", *ui8); |
320 |
|
|
break; |
321 |
|
|
|
322 |
|
|
case OFP_XM_T_IPV4_SRC: |
323 |
|
|
case OFP_XM_T_IPV4_DST: |
324 |
|
|
case OFP_XM_T_ARP_SPA: |
325 |
|
|
case OFP_XM_T_ARP_TPA: |
326 |
|
|
case OFP_XM_T_IPV6_FLABEL: |
327 |
|
|
len = sizeof(*ui32); |
328 |
|
|
if (hasmask) |
329 |
|
|
len *= 2; |
330 |
|
|
|
331 |
|
|
if ((ui32 = ibuf_seek(ibuf, off, len)) == NULL) |
332 |
|
|
return (-1); |
333 |
|
|
|
334 |
|
|
if (hasmask) |
335 |
|
|
log_debug("\t\t%#08x mask %#08x", |
336 |
|
|
ntohl(*ui32), ntohl(*(ui32 + 1))); |
337 |
|
|
else |
338 |
|
|
log_debug("\t\t%#08x", ntohl(*ui32)); |
339 |
|
|
break; |
340 |
|
|
|
341 |
|
|
case OFP_XM_T_IPV6_ND_TARGET: |
342 |
|
|
if (hasmask) |
343 |
|
|
return (-1); |
344 |
|
|
if ((ui8 = ibuf_seek(ibuf, off, |
345 |
|
|
sizeof(struct in6_addr))) == NULL) |
346 |
|
|
return (-1); |
347 |
|
|
|
348 |
|
|
buf[0] = 0; |
349 |
|
|
for (i = 0; i < (int)sizeof(struct in6_addr); i++) { |
350 |
|
|
snprintf(hex, sizeof(hex), "%02x", *(ui8 + i)); |
351 |
|
|
strlcat(buf, hex, sizeof(buf)); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
log_debug("\t\t%s", buf); |
355 |
|
|
break; |
356 |
|
|
|
357 |
|
|
case OFP_XM_T_IPV6_SRC: |
358 |
|
|
case OFP_XM_T_IPV6_DST: |
359 |
|
|
len = sizeof(struct in6_addr); |
360 |
|
|
if (hasmask) |
361 |
|
|
len *= 2; |
362 |
|
|
|
363 |
|
|
if ((ui8 = ibuf_seek(ibuf, off, len)) == NULL) |
364 |
|
|
return (-1); |
365 |
|
|
|
366 |
|
|
buf[0] = 0; |
367 |
|
|
for (i = 0; i < (int)sizeof(struct in6_addr); i++) { |
368 |
|
|
snprintf(hex, sizeof(hex), "%02x", *(ui8 + i)); |
369 |
|
|
strlcat(buf, hex, sizeof(buf)); |
370 |
|
|
} |
371 |
|
|
|
372 |
|
|
if (hasmask) { |
373 |
|
|
maskbuf[0] = 0; |
374 |
|
|
for (i = 0; i < (int)sizeof(struct in6_addr); i++) { |
375 |
|
|
snprintf(hex, sizeof(hex), "%02x", *(ui8 + |
376 |
|
|
(i + sizeof(struct in6_addr)))); |
377 |
|
|
strlcat(maskbuf, hex, sizeof(maskbuf)); |
378 |
|
|
} |
379 |
|
|
log_debug("\t\t%s mask %s", buf, maskbuf); |
380 |
|
|
} else |
381 |
|
|
log_debug("\t\t%s", buf); |
382 |
|
|
break; |
383 |
|
|
|
384 |
|
|
case OFP_XM_T_PBB_ISID: |
385 |
|
|
/* TODO teach me how to read 24 bits and convert to be. */ |
386 |
|
|
break; |
387 |
|
|
|
388 |
|
|
default: |
389 |
|
|
log_debug("\t\tUnknown type"); |
390 |
|
|
return (-1); |
391 |
|
|
} |
392 |
|
|
|
393 |
|
|
return (0); |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
int |
397 |
|
|
ofp13_validate_oxm(struct switchd *sc, struct ofp_ox_match *oxm, |
398 |
|
|
struct ofp_header *oh, struct ibuf *ibuf, off_t off) |
399 |
|
|
{ |
400 |
|
|
uint16_t class; |
401 |
|
|
uint8_t type; |
402 |
|
|
int hasmask; |
403 |
|
|
|
404 |
|
|
/* match element is always followed by data */ |
405 |
|
|
if (oxm->oxm_length == 0) |
406 |
|
|
return (0); |
407 |
|
|
|
408 |
|
|
type = OFP_OXM_GET_FIELD(oxm); |
409 |
|
|
hasmask = OFP_OXM_GET_HASMASK(oxm); |
410 |
|
|
class = ntohs(oxm->oxm_class); |
411 |
|
|
off += sizeof(*oxm); |
412 |
|
|
|
413 |
|
|
log_debug("\tox match class %s type %s hasmask %s length %u", |
414 |
|
|
print_map(class, ofp_oxm_c_map), |
415 |
|
|
print_map(type, ofp_xm_t_map), |
416 |
|
|
hasmask ? "yes" : "no", |
417 |
|
|
oxm->oxm_length); |
418 |
|
|
|
419 |
|
|
switch (class) { |
420 |
|
|
case OFP_OXM_C_NXM_0: |
421 |
|
|
case OFP_OXM_C_NXM_1: |
422 |
|
|
/* TODO teach me how to read NXM_*. */ |
423 |
|
|
break; |
424 |
|
|
|
425 |
|
|
case OFP_OXM_C_OPENFLOW_BASIC: |
426 |
|
|
return (ofp13_validate_oxm_basic(ibuf, off, hasmask, type)); |
427 |
|
|
|
428 |
|
|
case OFP_OXM_C_OPENFLOW_EXPERIMENTER: |
429 |
|
|
/* Implementation dependent: there is nothing to do here. */ |
430 |
|
|
break; |
431 |
|
|
|
432 |
|
|
default: |
433 |
|
|
return (-1); |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
return (0); |
437 |
|
|
} |
438 |
|
|
|
439 |
|
|
int |
440 |
|
|
ofp13_validate_packet_in(struct switchd *sc, |
441 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
442 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
443 |
|
|
{ |
444 |
|
|
struct ofp_packet_in *pin; |
445 |
|
|
struct ofp_match *om; |
446 |
|
|
struct ofp_ox_match *oxm; |
447 |
|
|
uint8_t *p; |
448 |
|
|
ssize_t len, mlen, plen; |
449 |
|
|
off_t moff, off; |
450 |
|
|
|
451 |
|
|
off = 0; |
452 |
|
|
if ((pin = ibuf_seek(ibuf, off, sizeof(*pin))) == NULL) |
453 |
|
|
return (-1); |
454 |
|
|
log_debug("\tbuffer %s length %u reason %s table %s cookie 0x%#016llx", |
455 |
|
|
print_map(ntohl(pin->pin_buffer_id), ofp_pktout_map), |
456 |
|
|
ntohs(pin->pin_total_len), |
457 |
|
|
print_map(ntohs(pin->pin_reason), ofp_pktin_map), |
458 |
|
|
print_map(pin->pin_table_id, ofp_table_id_map), |
459 |
|
|
be64toh(pin->pin_cookie)); |
460 |
|
|
off += offsetof(struct ofp_packet_in, pin_match); |
461 |
|
|
|
462 |
|
|
om = &pin->pin_match; |
463 |
|
|
mlen = ntohs(om->om_length); |
464 |
|
|
log_debug("\tmatch type %s length %zu (padded to %zu)", |
465 |
|
|
print_map(ntohs(om->om_type), ofp_match_map), |
466 |
|
|
mlen, OFP_ALIGN(mlen) + ETHER_ALIGN); |
467 |
|
|
mlen -= sizeof(*om); |
468 |
|
|
|
469 |
|
|
/* current match offset, aligned offset after all matches */ |
470 |
|
|
moff = off + sizeof(*om); |
471 |
|
|
off += OFP_ALIGN(mlen) + ETHER_ALIGN; |
472 |
|
|
|
473 |
|
|
switch (htons(om->om_type)) { |
474 |
|
|
case OFP_MATCH_OXM: |
475 |
|
|
do { |
476 |
|
|
if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm))) == NULL) |
477 |
|
|
return (-1); |
478 |
|
|
if (ofp13_validate_oxm(sc, oxm, oh, ibuf, moff) == -1) |
479 |
|
|
return (-1); |
480 |
|
|
moff += sizeof(*oxm) + oxm->oxm_length; |
481 |
|
|
mlen -= sizeof(*oxm) + oxm->oxm_length; |
482 |
|
|
} while (mlen > 0 && oxm->oxm_length); |
483 |
|
|
break; |
484 |
|
|
case OFP_MATCH_STANDARD: |
485 |
|
|
/* deprecated */ |
486 |
|
|
break; |
487 |
|
|
} |
488 |
|
|
|
489 |
|
|
len = ntohs(pin->pin_total_len); |
490 |
|
|
plen = ibuf_length(ibuf) - off; |
491 |
|
|
|
492 |
|
|
if (plen < len) { |
493 |
|
|
log_debug("\ttruncated packet %zu < %zu", plen, len); |
494 |
|
|
|
495 |
|
|
/* Buffered packets can be truncated */ |
496 |
|
|
if (pin->pin_buffer_id != OFP_PKTOUT_NO_BUFFER) |
497 |
|
|
len = plen; |
498 |
|
|
else |
499 |
|
|
return (-1); |
500 |
|
|
} |
501 |
|
|
if ((p = ibuf_seek(ibuf, off, len)) == NULL) |
502 |
|
|
return (-1); |
503 |
|
|
if (sc->sc_tap != -1) |
504 |
|
|
(void)write(sc->sc_tap, p, len); |
505 |
|
|
|
506 |
|
|
return (0); |
507 |
|
|
} |
508 |
|
|
|
509 |
|
|
int |
510 |
|
|
ofp13_validate_packet_out(struct switchd *sc, |
511 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
512 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
513 |
|
|
{ |
514 |
|
|
struct ofp_packet_out *pout; |
515 |
|
|
size_t len, plen, diff; |
516 |
|
|
off_t off, noff; |
517 |
|
|
struct ofp_action_header *ah; |
518 |
|
|
|
519 |
|
|
off = 0; |
520 |
|
|
if ((pout = ibuf_seek(ibuf, off, sizeof(*pout))) == NULL) { |
521 |
|
|
log_debug("%s: seek failed: length %zd", |
522 |
|
|
__func__, ibuf_length(ibuf)); |
523 |
|
|
return (-1); |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
off += sizeof(*pout); |
527 |
|
|
len = ntohs(pout->pout_actions_len); |
528 |
|
|
log_debug("\tbuffer %s in_port %s actions_len %lu", |
529 |
|
|
print_map(ntohl(pout->pout_buffer_id), ofp_pktout_map), |
530 |
|
|
print_map(ntohl(pout->pout_in_port), ofp_port_map), len); |
531 |
|
|
|
532 |
|
|
while (len > 0) { |
533 |
|
|
if ((ah = ibuf_seek(ibuf, off, sizeof(*ah))) == NULL) |
534 |
|
|
return (-1); |
535 |
|
|
|
536 |
|
|
noff = off; |
537 |
|
|
ofp13_validate_action(sc, oh, ibuf, &off, ah); |
538 |
|
|
|
539 |
|
|
diff = off - noff; |
540 |
|
|
/* Loop prevention. */ |
541 |
|
|
if (off < noff || diff == 0) |
542 |
|
|
return (-1); |
543 |
|
|
|
544 |
|
|
len -= diff; |
545 |
|
|
} |
546 |
|
|
|
547 |
|
|
/* Check for encapsulated packet truncation. */ |
548 |
|
|
len = ntohs(oh->oh_length) - off; |
549 |
|
|
plen = ibuf_length(ibuf) - off; |
550 |
|
|
|
551 |
|
|
if (plen < len) { |
552 |
|
|
log_debug("\ttruncated packet %lu < %lu", plen, len); |
553 |
|
|
|
554 |
|
|
/* Buffered packets can be truncated */ |
555 |
|
|
if (pout->pout_buffer_id != htonl(OFP_PKTOUT_NO_BUFFER)) |
556 |
|
|
len = plen; |
557 |
|
|
else |
558 |
|
|
return (-1); |
559 |
|
|
} |
560 |
|
|
if (ibuf_seek(ibuf, off, len) == NULL) |
561 |
|
|
return (-1); |
562 |
|
|
|
563 |
|
|
return (0); |
564 |
|
|
} |
565 |
|
|
|
566 |
|
|
int |
567 |
|
|
ofp13_validate_error(struct switchd *sc, |
568 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
569 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
570 |
|
|
{ |
571 |
|
|
struct ofp_error *err; |
572 |
|
|
off_t off; |
573 |
|
|
const char *code; |
574 |
|
|
|
575 |
|
|
off = 0; |
576 |
|
|
if ((err = ibuf_seek(ibuf, off, sizeof(*err))) == NULL) { |
577 |
|
|
log_debug("%s: seek failed: length %zd", |
578 |
|
|
__func__, ibuf_length(ibuf)); |
579 |
|
|
return (-1); |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
switch (ntohs(err->err_type)) { |
583 |
|
|
case OFP_ERRTYPE_FLOW_MOD_FAILED: |
584 |
|
|
code = print_map(ntohs(err->err_code), ofp_errflowmod_map); |
585 |
|
|
break; |
586 |
|
|
case OFP_ERRTYPE_BAD_MATCH: |
587 |
|
|
code = print_map(ntohs(err->err_code), ofp_errmatch_map); |
588 |
|
|
break; |
589 |
|
|
case OFP_ERRTYPE_BAD_INSTRUCTION: |
590 |
|
|
code = print_map(ntohs(err->err_code), ofp_errinst_map); |
591 |
|
|
break; |
592 |
|
|
case OFP_ERRTYPE_BAD_REQUEST: |
593 |
|
|
code = print_map(ntohs(err->err_code), ofp_errreq_map); |
594 |
|
|
break; |
595 |
|
|
default: |
596 |
|
|
code = NULL; |
597 |
|
|
break; |
598 |
|
|
} |
599 |
|
|
|
600 |
|
|
log_debug("\terror type %s code %u%s%s", |
601 |
|
|
print_map(ntohs(err->err_type), ofp_errtype_map), |
602 |
|
|
ntohs(err->err_code), |
603 |
|
|
code == NULL ? "" : ": ", |
604 |
|
|
code == NULL ? "" : code); |
605 |
|
|
|
606 |
|
|
return (0); |
607 |
|
|
} |
608 |
|
|
|
609 |
|
|
int |
610 |
|
|
ofp13_input(struct switchd *sc, struct switch_connection *con, |
611 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
612 |
|
|
{ |
613 |
|
|
if (ofp13_validate(sc, &con->con_peer, &con->con_local, oh, ibuf) != 0) |
614 |
|
|
return (-1); |
615 |
|
|
|
616 |
|
|
if (ofp13_callbacks[oh->oh_type].cb == NULL) { |
617 |
|
|
log_debug("%s: message not supported: %s", __func__, |
618 |
|
|
print_map(oh->oh_type, ofp_t_map)); |
619 |
|
|
return (-1); |
620 |
|
|
} |
621 |
|
|
if (ofp13_callbacks[oh->oh_type].cb(sc, con, oh, ibuf) != 0) { |
622 |
|
|
log_debug("%s: message parsing failed: %s", __func__, |
623 |
|
|
print_map(oh->oh_type, ofp_t_map)); |
624 |
|
|
return (-1); |
625 |
|
|
} |
626 |
|
|
|
627 |
|
|
return (0); |
628 |
|
|
} |
629 |
|
|
|
630 |
|
|
int |
631 |
|
|
ofp13_hello(struct switchd *sc, struct switch_connection *con, |
632 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
633 |
|
|
{ |
634 |
|
|
if (switch_add(con) == NULL) { |
635 |
|
|
log_debug("%s: failed to add switch", __func__); |
636 |
|
|
return (-1); |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
if (ofp_recv_hello(sc, con, oh, ibuf) == -1) |
640 |
|
|
return (-1); |
641 |
|
|
|
642 |
|
|
return (ofp_nextstate(sc, con, OFP_STATE_FEATURE_WAIT)); |
643 |
|
|
} |
644 |
|
|
|
645 |
|
|
int |
646 |
|
|
ofp13_echo_request(struct switchd *sc, struct switch_connection *con, |
647 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
648 |
|
|
{ |
649 |
|
|
/* Echo reply */ |
650 |
|
|
oh->oh_type = OFP_T_ECHO_REPLY; |
651 |
|
|
if (ofp13_validate(sc, &con->con_local, &con->con_peer, oh, NULL) != 0) |
652 |
|
|
return (-1); |
653 |
|
|
ofp_output(con, oh, NULL); |
654 |
|
|
|
655 |
|
|
return (0); |
656 |
|
|
} |
657 |
|
|
|
658 |
|
|
int |
659 |
|
|
ofp13_validate_features_reply(struct switchd *sc, |
660 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
661 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
662 |
|
|
{ |
663 |
|
|
struct ofp_switch_features *swf; |
664 |
|
|
|
665 |
|
|
if ((swf = ibuf_seek(ibuf, 0, sizeof(*swf))) == NULL) |
666 |
|
|
return (-1); |
667 |
|
|
|
668 |
|
|
log_debug("\tdatapath_id %#016llx nbuffers %u ntables %d aux_id %d " |
669 |
|
|
"capabilities %#08x", |
670 |
|
|
be64toh(swf->swf_datapath_id), ntohl(swf->swf_nbuffers), |
671 |
|
|
swf->swf_ntables, swf->swf_aux_id, ntohl(swf->swf_capabilities)); |
672 |
|
|
return (0); |
673 |
|
|
} |
674 |
|
|
|
675 |
|
|
int |
676 |
|
|
ofp13_features_reply(struct switchd *sc, struct switch_connection *con, |
677 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
678 |
|
|
{ |
679 |
|
|
return (ofp_nextstate(sc, con, OFP_STATE_ESTABLISHED)); |
680 |
|
|
} |
681 |
|
|
|
682 |
|
|
int |
683 |
|
|
ofp13_validate_action(struct switchd *sc, struct ofp_header *oh, |
684 |
|
|
struct ibuf *ibuf, off_t *off, struct ofp_action_header *ah) |
685 |
|
|
{ |
686 |
|
|
struct ofp_action_output *ao; |
687 |
|
|
struct ofp_action_mpls_ttl *amt; |
688 |
|
|
struct ofp_action_push *ap; |
689 |
|
|
struct ofp_action_pop_mpls *apm; |
690 |
|
|
struct ofp_action_group *ag; |
691 |
|
|
struct ofp_action_nw_ttl *ant; |
692 |
|
|
struct ofp_action_set_field *asf; |
693 |
|
|
struct ofp_action_set_queue *asq; |
694 |
|
|
struct ofp_ox_match *oxm; |
695 |
|
|
size_t len; |
696 |
|
|
int type; |
697 |
|
|
off_t moff; |
698 |
|
|
|
699 |
|
|
type = ntohs(ah->ah_type); |
700 |
|
|
len = ntohs(ah->ah_len); |
701 |
|
|
|
702 |
|
|
switch (type) { |
703 |
|
|
case OFP_ACTION_OUTPUT: |
704 |
|
|
if (len != sizeof(*ao)) |
705 |
|
|
return (-1); |
706 |
|
|
if ((ao = ibuf_seek(ibuf, *off, sizeof(*ao))) == NULL) |
707 |
|
|
return (-1); |
708 |
|
|
|
709 |
|
|
*off += len; |
710 |
|
|
log_debug("\t\taction %s len %lu port %s max_len %s", |
711 |
|
|
print_map(type, ofp_action_map), len, |
712 |
|
|
print_map(ntohl(ao->ao_port), ofp_port_map), |
713 |
|
|
print_map(ntohs(ao->ao_max_len), |
714 |
|
|
ofp_controller_maxlen_map)); |
715 |
|
|
break; |
716 |
|
|
case OFP_ACTION_SET_MPLS_TTL: |
717 |
|
|
if (len != sizeof(*amt)) |
718 |
|
|
return (-1); |
719 |
|
|
if ((amt = ibuf_seek(ibuf, *off, sizeof(*amt))) == NULL) |
720 |
|
|
return (-1); |
721 |
|
|
|
722 |
|
|
*off += len; |
723 |
|
|
log_debug("\t\taction %s len %lu ttl %d", |
724 |
|
|
print_map(type, ofp_action_map), len, amt->amt_ttl); |
725 |
|
|
break; |
726 |
|
|
case OFP_ACTION_PUSH_VLAN: |
727 |
|
|
case OFP_ACTION_PUSH_MPLS: |
728 |
|
|
case OFP_ACTION_PUSH_PBB: |
729 |
|
|
if (len != sizeof(*ap)) |
730 |
|
|
return (-1); |
731 |
|
|
if ((ap = ibuf_seek(ibuf, *off, sizeof(*ap))) == NULL) |
732 |
|
|
return (-1); |
733 |
|
|
|
734 |
|
|
*off += len; |
735 |
|
|
log_debug("\t\taction %s len %lu ethertype %#04x", |
736 |
|
|
print_map(type, ofp_action_map), len, |
737 |
|
|
ntohs(ap->ap_ethertype)); |
738 |
|
|
break; |
739 |
|
|
case OFP_ACTION_POP_MPLS: |
740 |
|
|
if (len != sizeof(*apm)) |
741 |
|
|
return (-1); |
742 |
|
|
if ((apm = ibuf_seek(ibuf, *off, sizeof(*apm))) == NULL) |
743 |
|
|
return (-1); |
744 |
|
|
|
745 |
|
|
*off += len; |
746 |
|
|
log_debug("\t\taction %s len %lu ethertype %#04x", |
747 |
|
|
print_map(type, ofp_action_map), len, |
748 |
|
|
ntohs(apm->apm_ethertype)); |
749 |
|
|
break; |
750 |
|
|
case OFP_ACTION_SET_QUEUE: |
751 |
|
|
if (len != sizeof(*asq)) |
752 |
|
|
return (-1); |
753 |
|
|
if ((asq = ibuf_seek(ibuf, *off, sizeof(*asq))) == NULL) |
754 |
|
|
return (-1); |
755 |
|
|
|
756 |
|
|
*off += len; |
757 |
|
|
log_debug("\t\taction %s len %lu queue_id %u", |
758 |
|
|
print_map(type, ofp_action_map), len, |
759 |
|
|
ntohl(asq->asq_queue_id)); |
760 |
|
|
break; |
761 |
|
|
case OFP_ACTION_GROUP: |
762 |
|
|
if (len != sizeof(*ag)) |
763 |
|
|
return (-1); |
764 |
|
|
if ((ag = ibuf_seek(ibuf, *off, sizeof(*ag))) == NULL) |
765 |
|
|
return (-1); |
766 |
|
|
|
767 |
|
|
*off += len; |
768 |
|
|
log_debug("\t\taction %s len %lu group_id %s", |
769 |
|
|
print_map(type, ofp_action_map), len, |
770 |
|
|
print_map(ntohl(ag->ag_group_id), ofp_group_id_map)); |
771 |
|
|
break; |
772 |
|
|
case OFP_ACTION_SET_NW_TTL: |
773 |
|
|
if (len != sizeof(*ant)) |
774 |
|
|
return (-1); |
775 |
|
|
if ((ant = ibuf_seek(ibuf, *off, sizeof(*ant))) == NULL) |
776 |
|
|
return (-1); |
777 |
|
|
|
778 |
|
|
*off += len; |
779 |
|
|
log_debug("\t\taction %s len %lu ttl %d", |
780 |
|
|
print_map(type, ofp_action_map), len, ant->ant_ttl); |
781 |
|
|
break; |
782 |
|
|
case OFP_ACTION_SET_FIELD: |
783 |
|
|
if (len < sizeof(*asf)) |
784 |
|
|
return (-1); |
785 |
|
|
if ((asf = ibuf_seek(ibuf, *off, sizeof(*asf))) == NULL) |
786 |
|
|
return (-1); |
787 |
|
|
|
788 |
|
|
moff = *off + sizeof(*asf) - sizeof(asf->asf_field); |
789 |
|
|
*off += len; |
790 |
|
|
log_debug("\t\taction %s len %lu", |
791 |
|
|
print_map(type, ofp_action_map), len); |
792 |
|
|
|
793 |
|
|
len -= sizeof(*asf) - sizeof(asf->asf_field); |
794 |
|
|
while (len > 0) { |
795 |
|
|
if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm))) |
796 |
|
|
== NULL) |
797 |
|
|
return (-1); |
798 |
|
|
if (ofp13_validate_oxm(sc, oxm, oh, ibuf, moff) == -1) |
799 |
|
|
return (-1); |
800 |
|
|
|
801 |
|
|
len -= sizeof(*oxm) + oxm->oxm_length; |
802 |
|
|
moff += sizeof(*oxm) + oxm->oxm_length; |
803 |
|
|
} |
804 |
|
|
break; |
805 |
|
|
|
806 |
|
|
default: |
807 |
|
|
if (len < sizeof(*ah)) |
808 |
|
|
return (-1); |
809 |
|
|
|
810 |
|
|
/* Generic header without information. */ |
811 |
|
|
*off += len; |
812 |
|
|
log_debug("\t\taction %s len %lu", |
813 |
|
|
print_map(type, ofp_action_map), len); |
814 |
|
|
break; |
815 |
|
|
} |
816 |
|
|
|
817 |
|
|
return (0); |
818 |
|
|
} |
819 |
|
|
|
820 |
|
|
int |
821 |
|
|
ofp13_validate_instruction(struct switchd *sc, struct ofp_header *oh, |
822 |
|
|
struct ibuf *ibuf, off_t *off, struct ofp_instruction *i) |
823 |
|
|
{ |
824 |
|
|
struct ofp_instruction_actions *ia; |
825 |
|
|
struct ofp_instruction_goto_table *igt; |
826 |
|
|
struct ofp_instruction_write_metadata *iwm; |
827 |
|
|
struct ofp_instruction_meter *im; |
828 |
|
|
struct ofp_action_header *ah; |
829 |
|
|
int type; |
830 |
|
|
size_t len; |
831 |
|
|
off_t oldoff, diff; |
832 |
|
|
|
833 |
|
|
type = ntohs(i->i_type); |
834 |
|
|
len = ntohs(i->i_len); |
835 |
|
|
|
836 |
|
|
switch (type) { |
837 |
|
|
case OFP_INSTRUCTION_T_GOTO_TABLE: |
838 |
|
|
if (len != sizeof(*igt)) |
839 |
|
|
return (-1); |
840 |
|
|
if ((igt = ibuf_seek(ibuf, *off, sizeof(*igt))) == NULL) |
841 |
|
|
return (-1); |
842 |
|
|
|
843 |
|
|
*off += len; |
844 |
|
|
log_debug("\tinstruction %s length %lu table_id %s", |
845 |
|
|
print_map(type, ofp_instruction_t_map), len, |
846 |
|
|
print_map(igt->igt_table_id, ofp_table_id_map)); |
847 |
|
|
break; |
848 |
|
|
case OFP_INSTRUCTION_T_WRITE_META: |
849 |
|
|
if (len != sizeof(*iwm)) |
850 |
|
|
return (-1); |
851 |
|
|
if ((iwm = ibuf_seek(ibuf, *off, sizeof(*iwm))) == NULL) |
852 |
|
|
return (-1); |
853 |
|
|
|
854 |
|
|
*off += len; |
855 |
|
|
log_debug("\tinstruction %s length %lu " |
856 |
|
|
"metadata %#016llx mask %#016llx", |
857 |
|
|
print_map(type, ofp_instruction_t_map), len, |
858 |
|
|
be64toh(iwm->iwm_metadata), |
859 |
|
|
be64toh(iwm->iwm_metadata_mask)); |
860 |
|
|
break; |
861 |
|
|
case OFP_INSTRUCTION_T_METER: |
862 |
|
|
if (len != sizeof(*im)) |
863 |
|
|
return (-1); |
864 |
|
|
if ((im = ibuf_seek(ibuf, *off, sizeof(*im))) == NULL) |
865 |
|
|
return (-1); |
866 |
|
|
|
867 |
|
|
*off += len; |
868 |
|
|
log_debug("\tinstruction %s length %lu meter_id %d", |
869 |
|
|
print_map(type, ofp_instruction_t_map), len, |
870 |
|
|
im->im_meter_id); |
871 |
|
|
break; |
872 |
|
|
case OFP_INSTRUCTION_T_WRITE_ACTIONS: |
873 |
|
|
case OFP_INSTRUCTION_T_CLEAR_ACTIONS: |
874 |
|
|
case OFP_INSTRUCTION_T_APPLY_ACTIONS: |
875 |
|
|
if (len < sizeof(*ia)) |
876 |
|
|
return (-1); |
877 |
|
|
if ((ia = ibuf_seek(ibuf, *off, sizeof(*ia))) == NULL) |
878 |
|
|
return (-1); |
879 |
|
|
|
880 |
|
|
log_debug("\tinstruction %s length %lu", |
881 |
|
|
print_map(type, ofp_instruction_t_map), len); |
882 |
|
|
|
883 |
|
|
*off += sizeof(*ia); |
884 |
|
|
len -= sizeof(*ia); |
885 |
|
|
while (len) { |
886 |
|
|
oldoff = *off; |
887 |
|
|
if ((ah = ibuf_seek(ibuf, *off, sizeof(*ah))) == NULL || |
888 |
|
|
ofp13_validate_action(sc, oh, ibuf, off, ah) == -1) |
889 |
|
|
return (-1); |
890 |
|
|
|
891 |
|
|
diff = *off - oldoff; |
892 |
|
|
/* Loop prevention. */ |
893 |
|
|
if (*off < oldoff || diff == 0) |
894 |
|
|
break; |
895 |
|
|
|
896 |
|
|
len -= diff; |
897 |
|
|
} |
898 |
|
|
break; |
899 |
|
|
default: |
900 |
|
|
if (len < sizeof(*i)) |
901 |
|
|
return (-1); |
902 |
|
|
|
903 |
|
|
log_debug("\tinstruction %s length %lu", |
904 |
|
|
print_map(type, ofp_instruction_t_map), len); |
905 |
|
|
*off += len; |
906 |
|
|
break; |
907 |
|
|
} |
908 |
|
|
|
909 |
|
|
return (0); |
910 |
|
|
} |
911 |
|
|
|
912 |
|
|
int |
913 |
|
|
ofp13_validate_flow_mod(struct switchd *sc, |
914 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
915 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
916 |
|
|
{ |
917 |
|
|
struct ofp_flow_mod *fm; |
918 |
|
|
struct ofp_match *om; |
919 |
|
|
struct ofp_instruction *i; |
920 |
|
|
struct ofp_ox_match *oxm; |
921 |
|
|
off_t off, moff, offdiff; |
922 |
|
|
int matchlen, matchtype, left; |
923 |
|
|
|
924 |
|
|
off = 0; |
925 |
|
|
if ((fm = ibuf_seek(ibuf, off, sizeof(*fm))) == NULL) |
926 |
|
|
return (-1); |
927 |
|
|
|
928 |
|
|
log_debug("\tcommand %s table %s timeout (idle %d hard %d) " |
929 |
|
|
"priority %d buffer_id %s out_port %s out_group %s " |
930 |
|
|
"flags %#04x cookie %#016llx mask %#016llx", |
931 |
|
|
print_map(fm->fm_command, ofp_flowcmd_map), |
932 |
|
|
print_map(fm->fm_table_id, ofp_table_id_map), |
933 |
|
|
ntohs(fm->fm_idle_timeout), ntohs(fm->fm_hard_timeout), |
934 |
|
|
ntohs(fm->fm_priority), |
935 |
|
|
print_map(ntohl(fm->fm_buffer_id), ofp_pktout_map), |
936 |
|
|
print_map(ntohl(fm->fm_out_port), ofp_port_map), |
937 |
|
|
print_map(ntohl(fm->fm_out_group), ofp_group_id_map), |
938 |
|
|
ntohs(fm->fm_flags), be64toh(fm->fm_cookie), |
939 |
|
|
be64toh(fm->fm_cookie_mask)); |
940 |
|
|
|
941 |
|
|
off += offsetof(struct ofp_flow_mod, fm_match); |
942 |
|
|
|
943 |
|
|
om = &fm->fm_match; |
944 |
|
|
matchtype = ntohs(om->om_type); |
945 |
|
|
matchlen = ntohs(om->om_length); |
946 |
|
|
|
947 |
|
|
moff = off + sizeof(*om); |
948 |
|
|
off += OFP_ALIGN(matchlen); |
949 |
|
|
|
950 |
|
|
matchlen -= sizeof(*om); |
951 |
|
|
while (matchlen) { |
952 |
|
|
if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm))) == NULL || |
953 |
|
|
ofp13_validate_oxm(sc, oxm, oh, ibuf, moff) == -1) |
954 |
|
|
return (-1); |
955 |
|
|
moff += sizeof(*oxm) + oxm->oxm_length; |
956 |
|
|
matchlen -= sizeof(*oxm) + oxm->oxm_length; |
957 |
|
|
} |
958 |
|
|
|
959 |
|
|
left = ntohs(oh->oh_length) - off; |
960 |
|
|
moff = off; |
961 |
|
|
while (left) { |
962 |
|
|
if ((i = ibuf_seek(ibuf, moff, sizeof(*i))) == NULL || |
963 |
|
|
ofp13_validate_instruction(sc, oh, ibuf, &moff, i) == -1) |
964 |
|
|
return (-1); |
965 |
|
|
|
966 |
|
|
offdiff = moff - off; |
967 |
|
|
/* Loop prevention. */ |
968 |
|
|
if (moff < off || offdiff == 0) |
969 |
|
|
break; |
970 |
|
|
|
971 |
|
|
left -= offdiff; |
972 |
|
|
off = moff; |
973 |
|
|
} |
974 |
|
|
|
975 |
|
|
return (0); |
976 |
|
|
} |
977 |
|
|
|
978 |
|
|
int |
979 |
|
|
ofp13_packet_match(struct ibuf *ibuf, struct packet *pkt, struct ofp_match *om) |
980 |
|
|
{ |
981 |
|
|
struct ether_header *eh = pkt->pkt_eh; |
982 |
|
|
size_t padsize, startpos, endpos, omlen; |
983 |
|
|
|
984 |
|
|
if (eh == NULL) |
985 |
|
|
return (-1); |
986 |
|
|
|
987 |
|
|
startpos = ibuf->wpos; |
988 |
|
|
if (oxm_etheraddr(ibuf, 1, eh->ether_shost, NULL) == -1) |
989 |
|
|
return (-1); |
990 |
|
|
if (oxm_etheraddr(ibuf, 0, eh->ether_dhost, NULL) == -1) |
991 |
|
|
return (-1); |
992 |
|
|
endpos = ibuf->wpos; |
993 |
|
|
|
994 |
|
|
omlen = sizeof(*om) + (endpos - startpos); |
995 |
|
|
padsize = OFP_ALIGN(omlen) - omlen; |
996 |
|
|
|
997 |
|
|
om->om_type = htons(OFP_MATCH_OXM); |
998 |
|
|
om->om_length = htons(omlen); |
999 |
|
|
if (padsize && ibuf_advance(ibuf, padsize) == NULL) |
1000 |
|
|
return (-1); |
1001 |
|
|
|
1002 |
|
|
return (0); |
1003 |
|
|
} |
1004 |
|
|
|
1005 |
|
|
int |
1006 |
|
|
ofp13_packet_in(struct switchd *sc, struct switch_connection *con, |
1007 |
|
|
struct ofp_header *ih, struct ibuf *ibuf) |
1008 |
|
|
{ |
1009 |
|
|
struct ofp_packet_in *pin; |
1010 |
|
|
struct ofp_packet_out *pout; |
1011 |
|
|
struct ofp_flow_mod *fm; |
1012 |
|
|
struct ofp_header *oh; |
1013 |
|
|
struct ofp_match *om; |
1014 |
|
|
struct ofp_ox_match *oxm; |
1015 |
|
|
struct packet pkt; |
1016 |
|
|
struct ibuf *obuf = NULL; |
1017 |
|
|
int table, ret = -1; |
1018 |
|
|
ssize_t len, mlen; |
1019 |
|
|
uint32_t srcport = 0, dstport; |
1020 |
|
|
int addflow = 0, sendbuffer = 0; |
1021 |
|
|
off_t off, moff; |
1022 |
|
|
void *ptr; |
1023 |
|
|
struct ofp_instruction_actions *ia; |
1024 |
|
|
|
1025 |
|
|
if ((pin = ibuf_getdata(ibuf, sizeof(*pin))) == NULL) |
1026 |
|
|
return (-1); |
1027 |
|
|
|
1028 |
|
|
/* We only handle no matches right now. */ |
1029 |
|
|
if (pin->pin_reason != OFP_PKTIN_REASON_NO_MATCH) |
1030 |
|
|
return (-1); |
1031 |
|
|
|
1032 |
|
|
bzero(&pkt, sizeof(pkt)); |
1033 |
|
|
len = ntohs(pin->pin_total_len); |
1034 |
|
|
|
1035 |
|
|
/* very basic way of getting the source port */ |
1036 |
|
|
om = &pin->pin_match; |
1037 |
|
|
mlen = ntohs(om->om_length); |
1038 |
|
|
off = (OFP_ALIGN(mlen) + ETHER_ALIGN) - sizeof(pin->pin_match); |
1039 |
|
|
moff = ibuf_dataoffset(ibuf); |
1040 |
|
|
|
1041 |
|
|
do { |
1042 |
|
|
if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm))) == NULL) |
1043 |
|
|
return (-1); |
1044 |
|
|
|
1045 |
|
|
/* Find IN_PORT */ |
1046 |
|
|
switch (ntohs(oxm->oxm_class)) { |
1047 |
|
|
case OFP_OXM_C_OPENFLOW_BASIC: |
1048 |
|
|
switch (OFP_OXM_GET_FIELD(oxm)) { |
1049 |
|
|
case OFP_XM_T_IN_PORT: |
1050 |
|
|
moff += sizeof(*oxm); |
1051 |
|
|
if ((ptr = ibuf_seek(ibuf, moff, |
1052 |
|
|
sizeof(srcport))) == NULL) |
1053 |
|
|
return (-1); |
1054 |
|
|
srcport = htonl(*(uint32_t *)ptr); |
1055 |
|
|
mlen = 0; /* break loop */ |
1056 |
|
|
break; |
1057 |
|
|
default: |
1058 |
|
|
/* ignore unsupported match types */ |
1059 |
|
|
break; |
1060 |
|
|
} |
1061 |
|
|
default: |
1062 |
|
|
/* ignore unsupported match classes */ |
1063 |
|
|
break; |
1064 |
|
|
} |
1065 |
|
|
moff += sizeof(*oxm) + oxm->oxm_length; |
1066 |
|
|
mlen -= sizeof(*oxm) + oxm->oxm_length; |
1067 |
|
|
} while (mlen > 0 && oxm->oxm_length); |
1068 |
|
|
|
1069 |
|
|
/* Skip all matches and seek to the packet */ |
1070 |
|
|
if (ibuf_getdata(ibuf, off) == NULL) |
1071 |
|
|
return (-1); |
1072 |
|
|
|
1073 |
|
|
if (packet_input(sc, con->con_switch, |
1074 |
|
|
srcport, &dstport, ibuf, len, &pkt) == -1 || |
1075 |
|
|
(dstport > OFP_PORT_MAX && |
1076 |
|
|
dstport != OFP_PORT_LOCAL && |
1077 |
|
|
dstport != OFP_PORT_CONTROLLER)) { |
1078 |
|
|
/* fallback to flooding */ |
1079 |
|
|
dstport = OFP_PORT_FLOOD; |
1080 |
|
|
} else if (srcport == dstport) { |
1081 |
|
|
/* |
1082 |
|
|
* silently drop looping packet |
1083 |
|
|
* (don't use OFP_PORT_INPUT here) |
1084 |
|
|
*/ |
1085 |
|
|
dstport = OFP_PORT_ANY; |
1086 |
|
|
} else { |
1087 |
|
|
addflow = 1; |
1088 |
|
|
} |
1089 |
|
|
|
1090 |
|
|
if ((obuf = ibuf_static()) == NULL) |
1091 |
|
|
goto done; |
1092 |
|
|
|
1093 |
|
|
again: |
1094 |
|
|
if (addflow) { |
1095 |
|
|
table = ofp13_getflowtable(con); |
1096 |
|
|
if (table > OFP_TABLE_ID_MAX || table < 0) { |
1097 |
|
|
/* This switch doesn't support installing flows. */ |
1098 |
|
|
addflow = 0; |
1099 |
|
|
goto again; |
1100 |
|
|
} |
1101 |
|
|
|
1102 |
|
|
if ((fm = ibuf_advance(obuf, sizeof(*fm))) == NULL) |
1103 |
|
|
goto done; |
1104 |
|
|
|
1105 |
|
|
oh = &fm->fm_oh; |
1106 |
|
|
fm->fm_cookie = 0; /* XXX should we set a cookie? */ |
1107 |
|
|
fm->fm_command = OFP_FLOWCMD_ADD; |
1108 |
|
|
fm->fm_idle_timeout = htons(sc->sc_cache_timeout); |
1109 |
|
|
fm->fm_hard_timeout = 0; /* permanent */ |
1110 |
|
|
fm->fm_priority = 0; |
1111 |
|
|
fm->fm_buffer_id = pin->pin_buffer_id; |
1112 |
|
|
fm->fm_table_id = table; |
1113 |
|
|
fm->fm_flags = htons(OFP_FLOWFLAG_SEND_FLOW_REMOVED); |
1114 |
|
|
if (pin->pin_buffer_id == htonl(OFP_PKTOUT_NO_BUFFER)) |
1115 |
|
|
sendbuffer = 1; |
1116 |
|
|
|
1117 |
|
|
/* Write flow matches to create an entry. */ |
1118 |
|
|
if (ofp13_packet_match(obuf, &pkt, &fm->fm_match) == -1) |
1119 |
|
|
goto done; |
1120 |
|
|
|
1121 |
|
|
/* |
1122 |
|
|
* Write the instruction action header and add the output |
1123 |
|
|
* action. |
1124 |
|
|
*/ |
1125 |
|
|
if ((ia = ibuf_advance(obuf, sizeof(*ia))) == NULL || |
1126 |
|
|
action_output(obuf, dstport, |
1127 |
|
|
OFP_CONTROLLER_MAXLEN_NO_BUFFER) == -1) |
1128 |
|
|
goto done; |
1129 |
|
|
|
1130 |
|
|
ia->ia_type = htons(OFP_INSTRUCTION_T_APPLY_ACTIONS); |
1131 |
|
|
ia->ia_len = htons(sizeof(*ia) + |
1132 |
|
|
sizeof(struct ofp_action_output)); |
1133 |
|
|
} else { |
1134 |
|
|
if ((pout = ibuf_advance(obuf, sizeof(*pout))) == NULL) |
1135 |
|
|
goto done; |
1136 |
|
|
|
1137 |
|
|
oh = &pout->pout_oh; |
1138 |
|
|
pout->pout_buffer_id = pin->pin_buffer_id; |
1139 |
|
|
pout->pout_in_port = htonl(srcport); |
1140 |
|
|
pout->pout_actions_len = |
1141 |
|
|
htons(sizeof(struct ofp_action_output)); |
1142 |
|
|
|
1143 |
|
|
if (action_output(obuf, dstport, |
1144 |
|
|
OFP_CONTROLLER_MAXLEN_NO_BUFFER) == -1) |
1145 |
|
|
goto done; |
1146 |
|
|
|
1147 |
|
|
/* Add optional packet payload */ |
1148 |
|
|
if (pin->pin_buffer_id == htonl(OFP_PKTOUT_NO_BUFFER) && |
1149 |
|
|
imsg_add(obuf, pkt.pkt_buf, pkt.pkt_len) == -1) |
1150 |
|
|
goto done; |
1151 |
|
|
} |
1152 |
|
|
|
1153 |
|
|
/* Set output header */ |
1154 |
|
|
oh->oh_version = OFP_V_1_3; |
1155 |
|
|
oh->oh_length = htons(ibuf_length(obuf)); |
1156 |
|
|
oh->oh_type = addflow ? OFP_T_FLOW_MOD : OFP_T_PACKET_OUT; |
1157 |
|
|
oh->oh_xid = htonl(con->con_xidnxt++); |
1158 |
|
|
|
1159 |
|
|
if (ofp13_validate(sc, &con->con_local, &con->con_peer, oh, obuf) != 0) |
1160 |
|
|
goto done; |
1161 |
|
|
|
1162 |
|
|
ofp_output(con, NULL, obuf); |
1163 |
|
|
|
1164 |
|
|
if (sendbuffer) { |
1165 |
|
|
ibuf_release(obuf); |
1166 |
|
|
|
1167 |
|
|
/* loop to output the packet again */ |
1168 |
|
|
addflow = sendbuffer = 0; |
1169 |
|
|
if ((obuf = ibuf_static()) == NULL) |
1170 |
|
|
goto done; |
1171 |
|
|
goto again; |
1172 |
|
|
} |
1173 |
|
|
|
1174 |
|
|
ret = 0; |
1175 |
|
|
done: |
1176 |
|
|
ibuf_release(obuf); |
1177 |
|
|
return (ret); |
1178 |
|
|
} |
1179 |
|
|
|
1180 |
|
|
int |
1181 |
|
|
ofp13_flow_removed(struct switchd *sc, struct switch_connection *con, |
1182 |
|
|
struct ofp_header *ih, struct ibuf *ibuf) |
1183 |
|
|
{ |
1184 |
|
|
struct ofp_flow_removed *fr; |
1185 |
|
|
|
1186 |
|
|
if ((fr = ibuf_getdata(ibuf, sizeof(*fr))) == NULL) |
1187 |
|
|
return (-1); |
1188 |
|
|
|
1189 |
|
|
log_debug("cookie %#016llx priority %d reason %s table_id %s " |
1190 |
|
|
"duration(%u sec, %u nsec) timeout idle %d hard %d " |
1191 |
|
|
"packet %llu byte %llu", |
1192 |
|
|
be64toh(fr->fr_cookie), ntohs(fr->fr_priority), |
1193 |
|
|
print_map(fr->fr_reason, ofp_flowrem_reason_map), |
1194 |
|
|
print_map(fr->fr_table_id, ofp_table_id_map), |
1195 |
|
|
ntohl(fr->fr_duration_sec), ntohl(fr->fr_duration_nsec), |
1196 |
|
|
ntohs(fr->fr_idle_timeout), ntohs(fr->fr_hard_timeout), |
1197 |
|
|
be64toh(fr->fr_packet_count), be64toh(fr->fr_byte_count)); |
1198 |
|
|
|
1199 |
|
|
return (0); |
1200 |
|
|
} |
1201 |
|
|
|
1202 |
|
|
int |
1203 |
|
|
ofp13_tableproperties(struct switch_connection *con, struct ibuf *ibuf, |
1204 |
|
|
off_t off, size_t total, int new) |
1205 |
|
|
{ |
1206 |
|
|
struct ofp_table_features *tf; |
1207 |
|
|
struct ofp_table_feature_property *tp; |
1208 |
|
|
struct ofp_instruction *i; |
1209 |
|
|
struct ofp_action_header *ah; |
1210 |
|
|
struct ofp_ox_match *oxm; |
1211 |
|
|
struct switch_table *st; |
1212 |
|
|
uint8_t *next_table; |
1213 |
|
|
int remaining, type, length; |
1214 |
|
|
int hlen, padsize; |
1215 |
|
|
int class, dtype, dlen; |
1216 |
|
|
|
1217 |
|
|
/* |
1218 |
|
|
* This is a new table features reply, free our previous tables |
1219 |
|
|
* to get the updated ones. |
1220 |
|
|
*/ |
1221 |
|
|
if (new) |
1222 |
|
|
switch_freetables(con); |
1223 |
|
|
|
1224 |
|
|
next_table: |
1225 |
|
|
if ((tf = ibuf_seek(ibuf, off, sizeof(*tf))) == NULL) |
1226 |
|
|
return (-1); |
1227 |
|
|
|
1228 |
|
|
hlen = htons(tf->tf_length); |
1229 |
|
|
total -= hlen; |
1230 |
|
|
remaining = hlen - sizeof(*tf); |
1231 |
|
|
off += sizeof(*tf); |
1232 |
|
|
|
1233 |
|
|
st = switch_tablelookup(con, tf->tf_tableid); |
1234 |
|
|
if (st == NULL) { |
1235 |
|
|
st = switch_newtable(con, tf->tf_tableid); |
1236 |
|
|
if (st == NULL) |
1237 |
|
|
return (-1); |
1238 |
|
|
} |
1239 |
|
|
|
1240 |
|
|
st->st_maxentries = ntohl(tf->tf_max_entries); |
1241 |
|
|
|
1242 |
|
|
next_table_property: |
1243 |
|
|
if ((tp = ibuf_seek(ibuf, off, sizeof(*tp))) == NULL) { |
1244 |
|
|
switch_deltable(con, st); |
1245 |
|
|
return (-1); |
1246 |
|
|
} |
1247 |
|
|
|
1248 |
|
|
type = ntohs(tp->tp_type); |
1249 |
|
|
length = ntohs(tp->tp_length); |
1250 |
|
|
|
1251 |
|
|
/* Calculate the padding. */ |
1252 |
|
|
padsize = OFP_ALIGN(length) - length; |
1253 |
|
|
remaining -= OFP_ALIGN(length); |
1254 |
|
|
length -= sizeof(*tp); |
1255 |
|
|
off += sizeof(*tp); |
1256 |
|
|
|
1257 |
|
|
switch (type) { |
1258 |
|
|
case OFP_TABLE_FEATPROP_INSTRUCTION: |
1259 |
|
|
case OFP_TABLE_FEATPROP_INSTRUCTION_MISS: |
1260 |
|
|
if (type == OFP_TABLE_FEATPROP_INSTRUCTION) |
1261 |
|
|
st->st_instructions = 0; |
1262 |
|
|
else |
1263 |
|
|
st->st_instructionsmiss = 0; |
1264 |
|
|
|
1265 |
|
|
while (length) { |
1266 |
|
|
if ((i = ibuf_seek(ibuf, off, sizeof(*i))) == NULL) { |
1267 |
|
|
switch_deltable(con, st); |
1268 |
|
|
return (-1); |
1269 |
|
|
} |
1270 |
|
|
|
1271 |
|
|
dtype = ntohs(i->i_type); |
1272 |
|
|
dlen = ntohs(i->i_len); |
1273 |
|
|
if (type == OFP_TABLE_FEATPROP_INSTRUCTION) |
1274 |
|
|
st->st_instructions |= 1ULL << dtype; |
1275 |
|
|
else |
1276 |
|
|
st->st_instructionsmiss |= 1ULL << dtype; |
1277 |
|
|
|
1278 |
|
|
if (dtype == OFP_INSTRUCTION_T_EXPERIMENTER) { |
1279 |
|
|
length -= dlen; |
1280 |
|
|
off += dlen; |
1281 |
|
|
} else { |
1282 |
|
|
length -= sizeof(*i); |
1283 |
|
|
off += sizeof(*i); |
1284 |
|
|
} |
1285 |
|
|
} |
1286 |
|
|
break; |
1287 |
|
|
|
1288 |
|
|
case OFP_TABLE_FEATPROP_NEXT_TABLES: |
1289 |
|
|
case OFP_TABLE_FEATPROP_NEXT_TABLES_MISS: |
1290 |
|
|
if (type == OFP_TABLE_FEATPROP_NEXT_TABLES) |
1291 |
|
|
memset(st->st_nexttable, 0, sizeof(st->st_nexttable)); |
1292 |
|
|
else |
1293 |
|
|
memset(st->st_nexttablemiss, 0, |
1294 |
|
|
sizeof(st->st_nexttablemiss)); |
1295 |
|
|
|
1296 |
|
|
while (length) { |
1297 |
|
|
if ((next_table = ibuf_seek(ibuf, off, |
1298 |
|
|
sizeof(*next_table))) == NULL) { |
1299 |
|
|
switch_deltable(con, st); |
1300 |
|
|
return (-1); |
1301 |
|
|
} |
1302 |
|
|
|
1303 |
|
|
if (type == OFP_TABLE_FEATPROP_NEXT_TABLES) |
1304 |
|
|
st->st_nexttable[(*next_table) / 64] |= |
1305 |
|
|
1ULL << ((*next_table) % 64); |
1306 |
|
|
else |
1307 |
|
|
st->st_nexttablemiss[(*next_table) / 64] |= |
1308 |
|
|
1ULL << ((*next_table) % 64); |
1309 |
|
|
|
1310 |
|
|
length -= sizeof(*next_table); |
1311 |
|
|
off += sizeof(*next_table); |
1312 |
|
|
} |
1313 |
|
|
break; |
1314 |
|
|
|
1315 |
|
|
case OFP_TABLE_FEATPROP_WRITE_ACTIONS: |
1316 |
|
|
case OFP_TABLE_FEATPROP_WRITE_ACTIONS_MISS: |
1317 |
|
|
case OFP_TABLE_FEATPROP_APPLY_ACTIONS: |
1318 |
|
|
case OFP_TABLE_FEATPROP_APPLY_ACTIONS_MISS: |
1319 |
|
|
if (type == OFP_TABLE_FEATPROP_WRITE_ACTIONS || |
1320 |
|
|
type == OFP_TABLE_FEATPROP_APPLY_ACTIONS) |
1321 |
|
|
st->st_actions = 0; |
1322 |
|
|
else |
1323 |
|
|
st->st_actionsmiss = 0; |
1324 |
|
|
|
1325 |
|
|
while (length) { |
1326 |
|
|
/* |
1327 |
|
|
* NOTE the OpenFlow 1.3.5 specs says that we only |
1328 |
|
|
* get 4 bytes here instead of the full action header. |
1329 |
|
|
*/ |
1330 |
|
|
if ((ah = ibuf_seek(ibuf, off, 4)) == NULL) { |
1331 |
|
|
switch_deltable(con, st); |
1332 |
|
|
return (-1); |
1333 |
|
|
} |
1334 |
|
|
|
1335 |
|
|
dtype = ntohs(ah->ah_type); |
1336 |
|
|
dlen = ntohs(ah->ah_len); |
1337 |
|
|
if (type == OFP_TABLE_FEATPROP_WRITE_ACTIONS || |
1338 |
|
|
type == OFP_TABLE_FEATPROP_APPLY_ACTIONS) |
1339 |
|
|
st->st_actions |= 1ULL << dtype; |
1340 |
|
|
else |
1341 |
|
|
st->st_actionsmiss |= 1ULL << dtype; |
1342 |
|
|
|
1343 |
|
|
if (dtype == OFP_ACTION_EXPERIMENTER) { |
1344 |
|
|
length -= dlen; |
1345 |
|
|
off += dlen; |
1346 |
|
|
} else { |
1347 |
|
|
length -= 4; |
1348 |
|
|
off += 4; |
1349 |
|
|
} |
1350 |
|
|
} |
1351 |
|
|
break; |
1352 |
|
|
|
1353 |
|
|
case OFP_TABLE_FEATPROP_MATCH: |
1354 |
|
|
case OFP_TABLE_FEATPROP_WILDCARDS: |
1355 |
|
|
case OFP_TABLE_FEATPROP_WRITE_SETFIELD: |
1356 |
|
|
case OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS: |
1357 |
|
|
case OFP_TABLE_FEATPROP_APPLY_SETFIELD: |
1358 |
|
|
case OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS: |
1359 |
|
|
if (type == OFP_TABLE_FEATPROP_WRITE_SETFIELD || |
1360 |
|
|
type == OFP_TABLE_FEATPROP_APPLY_SETFIELD) |
1361 |
|
|
st->st_setfield = 0; |
1362 |
|
|
else if (type == OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS || |
1363 |
|
|
type == OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS) |
1364 |
|
|
st->st_setfieldmiss = 0; |
1365 |
|
|
else if (type == OFP_TABLE_FEATPROP_MATCH) |
1366 |
|
|
st->st_match = 0; |
1367 |
|
|
else |
1368 |
|
|
st->st_wildcard = 0; |
1369 |
|
|
|
1370 |
|
|
while (length) { |
1371 |
|
|
if ((oxm = ibuf_seek(ibuf, off, |
1372 |
|
|
sizeof(*oxm))) == NULL) { |
1373 |
|
|
switch_deltable(con, st); |
1374 |
|
|
return (-1); |
1375 |
|
|
} |
1376 |
|
|
|
1377 |
|
|
class = ntohs(oxm->oxm_class); |
1378 |
|
|
if (class != OFP_OXM_C_OPENFLOW_BASIC) { |
1379 |
|
|
if (class == OFP_OXM_C_OPENFLOW_EXPERIMENTER) { |
1380 |
|
|
length -= sizeof(*oxm) + 4; |
1381 |
|
|
off += sizeof(*oxm) + 4; |
1382 |
|
|
} else { |
1383 |
|
|
length -= sizeof(*oxm); |
1384 |
|
|
off += sizeof(*oxm); |
1385 |
|
|
} |
1386 |
|
|
continue; |
1387 |
|
|
} |
1388 |
|
|
|
1389 |
|
|
dtype = OFP_OXM_GET_FIELD(oxm); |
1390 |
|
|
if (type == OFP_TABLE_FEATPROP_WRITE_SETFIELD || |
1391 |
|
|
type == OFP_TABLE_FEATPROP_APPLY_SETFIELD) |
1392 |
|
|
st->st_setfield |= 1ULL << dtype; |
1393 |
|
|
else if |
1394 |
|
|
(type == OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS || |
1395 |
|
|
type == OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS) |
1396 |
|
|
st->st_setfieldmiss |= 1ULL << dtype; |
1397 |
|
|
else if (type == OFP_TABLE_FEATPROP_MATCH) |
1398 |
|
|
st->st_match |= 1ULL << dtype; |
1399 |
|
|
else |
1400 |
|
|
st->st_wildcard |= 1ULL << dtype; |
1401 |
|
|
|
1402 |
|
|
length -= sizeof(*oxm); |
1403 |
|
|
off += sizeof(*oxm); |
1404 |
|
|
} |
1405 |
|
|
break; |
1406 |
|
|
|
1407 |
|
|
case OFP_TABLE_FEATPROP_EXPERIMENTER: |
1408 |
|
|
case OFP_TABLE_FEATPROP_EXPERIMENTER_MISS: |
1409 |
|
|
off += length; |
1410 |
|
|
break; |
1411 |
|
|
|
1412 |
|
|
default: |
1413 |
|
|
log_debug("Unsupported table property %d", type); |
1414 |
|
|
return (-1); |
1415 |
|
|
} |
1416 |
|
|
|
1417 |
|
|
if (padsize) |
1418 |
|
|
off += padsize; |
1419 |
|
|
if (remaining > 0) |
1420 |
|
|
goto next_table_property; |
1421 |
|
|
if (total > 0) |
1422 |
|
|
goto next_table; |
1423 |
|
|
|
1424 |
|
|
return (0); |
1425 |
|
|
} |
1426 |
|
|
|
1427 |
|
|
int |
1428 |
|
|
ofp13_multipart_reply(struct switchd *sc, struct switch_connection *con, |
1429 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
1430 |
|
|
{ |
1431 |
|
|
struct ofp_multipart *mp; |
1432 |
|
|
int type, flags, more, new = 0; |
1433 |
|
|
int remaining; |
1434 |
|
|
off_t off; |
1435 |
|
|
|
1436 |
|
|
off = 0; |
1437 |
|
|
if ((mp = ibuf_seek(ibuf, 0, sizeof(*mp))) == NULL) |
1438 |
|
|
return (-1); |
1439 |
|
|
|
1440 |
|
|
type = ntohs(mp->mp_type); |
1441 |
|
|
flags = ntohs(mp->mp_flags); |
1442 |
|
|
remaining = ntohs(oh->oh_length) - sizeof(*mp); |
1443 |
|
|
off += sizeof(*mp); |
1444 |
|
|
|
1445 |
|
|
more = (flags & OFP_MP_FLAG_REPLY_MORE) == OFP_MP_FLAG_REPLY_MORE; |
1446 |
|
|
/* Signalize new requests. */ |
1447 |
|
|
if (ofp_multipart_lookup(con, oh->oh_xid) == NULL) |
1448 |
|
|
new = 1; |
1449 |
|
|
|
1450 |
|
|
if (more) { |
1451 |
|
|
if (ofp_multipart_add(con, oh->oh_xid, type) == -1) { |
1452 |
|
|
ofp13_error(sc, con, oh, ibuf, |
1453 |
|
|
OFP_ERRTYPE_BAD_REQUEST, |
1454 |
|
|
OFP_ERRREQ_MULTIPART_OVERFLOW); |
1455 |
|
|
ofp_multipart_del(con, oh->oh_xid); |
1456 |
|
|
return (0); |
1457 |
|
|
} |
1458 |
|
|
|
1459 |
|
|
/* |
1460 |
|
|
* We don't need to concatenate with other messages, |
1461 |
|
|
* because the specification says that switches don't |
1462 |
|
|
* break objects. We should only need this if our parser |
1463 |
|
|
* requires the whole data before hand, but then we have |
1464 |
|
|
* better ways to achieve the same thing. |
1465 |
|
|
*/ |
1466 |
|
|
} else |
1467 |
|
|
ofp_multipart_del(con, oh->oh_xid); |
1468 |
|
|
|
1469 |
|
|
switch (type) { |
1470 |
|
|
case OFP_MP_T_TABLE_FEATURES: |
1471 |
|
|
if (ofp13_tableproperties(con, ibuf, off, remaining, new)) |
1472 |
|
|
return (-1); |
1473 |
|
|
|
1474 |
|
|
/* We finished receiving tables, configure the switch. */ |
1475 |
|
|
if (more == 0) |
1476 |
|
|
return (ofp13_switchconfigure(sc, con)); |
1477 |
|
|
break; |
1478 |
|
|
} |
1479 |
|
|
|
1480 |
|
|
return (0); |
1481 |
|
|
} |
1482 |
|
|
|
1483 |
|
|
int |
1484 |
|
|
ofp13_validate_tableproperty(struct ibuf *ibuf, off_t off, int remaining) |
1485 |
|
|
{ |
1486 |
|
|
struct ofp_table_features *tf; |
1487 |
|
|
struct ofp_table_feature_property *tp; |
1488 |
|
|
struct ofp_instruction *i; |
1489 |
|
|
struct ofp_action_header *ah; |
1490 |
|
|
struct ofp_ox_match *oxm; |
1491 |
|
|
uint8_t *nexttable; |
1492 |
|
|
int hlen, htype, tplen; |
1493 |
|
|
int type, length, class; |
1494 |
|
|
int padsize; |
1495 |
|
|
|
1496 |
|
|
next_table: |
1497 |
|
|
if ((tf = ibuf_seek(ibuf, off, sizeof(*tf))) == NULL) |
1498 |
|
|
return (-1); |
1499 |
|
|
|
1500 |
|
|
hlen = ntohs(tf->tf_length); |
1501 |
|
|
log_debug("\t\ttable features length %d tableid %s " |
1502 |
|
|
" name \"%s\" metadata match %#016llx write %#016llx " |
1503 |
|
|
"config %u max_entries %u", |
1504 |
|
|
hlen, print_map(tf->tf_tableid, ofp_table_id_map), tf->tf_name, |
1505 |
|
|
be64toh(tf->tf_metadata_match), |
1506 |
|
|
be64toh(tf->tf_metadata_write), ntohl(tf->tf_config), |
1507 |
|
|
ntohl(tf->tf_max_entries)); |
1508 |
|
|
|
1509 |
|
|
off += sizeof(*tf); |
1510 |
|
|
remaining -= sizeof(*tf); |
1511 |
|
|
hlen -= sizeof(*tf); |
1512 |
|
|
|
1513 |
|
|
next_property: |
1514 |
|
|
if ((tp = ibuf_seek(ibuf, off, sizeof(*tp))) == NULL) |
1515 |
|
|
return (-1); |
1516 |
|
|
|
1517 |
|
|
off += sizeof(*tp); |
1518 |
|
|
htype = ntohs(tp->tp_type); |
1519 |
|
|
tplen = ntohs(tp->tp_length); |
1520 |
|
|
padsize = OFP_ALIGN(tplen) - tplen; |
1521 |
|
|
remaining -= tplen; |
1522 |
|
|
hlen -= tplen; |
1523 |
|
|
|
1524 |
|
|
/* Don't count the header bytes for payload. */ |
1525 |
|
|
tplen -= sizeof(*tp); |
1526 |
|
|
|
1527 |
|
|
log_debug("\t\t%s (length %d):", |
1528 |
|
|
print_map(htype, ofp_table_featprop_map), tplen); |
1529 |
|
|
if (tplen <= 0) |
1530 |
|
|
goto empty_table; |
1531 |
|
|
|
1532 |
|
|
switch (htype) { |
1533 |
|
|
case OFP_TABLE_FEATPROP_INSTRUCTION: |
1534 |
|
|
case OFP_TABLE_FEATPROP_INSTRUCTION_MISS: |
1535 |
|
|
while (tplen > 0) { |
1536 |
|
|
if ((i = ibuf_seek(ibuf, off, sizeof(*i))) == NULL) |
1537 |
|
|
return (-1); |
1538 |
|
|
|
1539 |
|
|
type = ntohs(i->i_type); |
1540 |
|
|
length = ntohs(i->i_len); |
1541 |
|
|
if (type == OFP_INSTRUCTION_T_EXPERIMENTER) { |
1542 |
|
|
tplen -= length; |
1543 |
|
|
off += length; |
1544 |
|
|
} else { |
1545 |
|
|
tplen -= sizeof(*i); |
1546 |
|
|
off += sizeof(*i); |
1547 |
|
|
} |
1548 |
|
|
|
1549 |
|
|
log_debug("\t\t\ttype %s length %d", |
1550 |
|
|
print_map(type, ofp_instruction_t_map), length); |
1551 |
|
|
} |
1552 |
|
|
break; |
1553 |
|
|
|
1554 |
|
|
case OFP_TABLE_FEATPROP_NEXT_TABLES: |
1555 |
|
|
case OFP_TABLE_FEATPROP_NEXT_TABLES_MISS: |
1556 |
|
|
while (tplen > 0) { |
1557 |
|
|
if ((nexttable = ibuf_seek(ibuf, off, |
1558 |
|
|
sizeof(*nexttable))) == NULL) |
1559 |
|
|
return (-1); |
1560 |
|
|
|
1561 |
|
|
log_debug("\t\t\t%d", *nexttable); |
1562 |
|
|
|
1563 |
|
|
off += sizeof(*nexttable); |
1564 |
|
|
tplen -= sizeof(*nexttable); |
1565 |
|
|
} |
1566 |
|
|
break; |
1567 |
|
|
|
1568 |
|
|
case OFP_TABLE_FEATPROP_WRITE_ACTIONS: |
1569 |
|
|
case OFP_TABLE_FEATPROP_WRITE_ACTIONS_MISS: |
1570 |
|
|
case OFP_TABLE_FEATPROP_APPLY_ACTIONS: |
1571 |
|
|
case OFP_TABLE_FEATPROP_APPLY_ACTIONS_MISS: |
1572 |
|
|
while (tplen > 0) { |
1573 |
|
|
/* NOTE: we read the action header without the pad. */ |
1574 |
|
|
if ((ah = ibuf_seek(ibuf, off, 4)) == NULL) |
1575 |
|
|
return (-1); |
1576 |
|
|
|
1577 |
|
|
type = ntohs(ah->ah_type); |
1578 |
|
|
length = ntohs(ah->ah_len); |
1579 |
|
|
log_debug("\t\t\taction %s length %d", |
1580 |
|
|
print_map(type, ofp_action_map), length); |
1581 |
|
|
if (type == OFP_ACTION_EXPERIMENTER) { |
1582 |
|
|
tplen -= length; |
1583 |
|
|
off += length; |
1584 |
|
|
} else { |
1585 |
|
|
tplen -= 4; |
1586 |
|
|
off += 4; |
1587 |
|
|
} |
1588 |
|
|
} |
1589 |
|
|
break; |
1590 |
|
|
|
1591 |
|
|
case OFP_TABLE_FEATPROP_MATCH: |
1592 |
|
|
case OFP_TABLE_FEATPROP_WILDCARDS: |
1593 |
|
|
case OFP_TABLE_FEATPROP_WRITE_SETFIELD: |
1594 |
|
|
case OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS: |
1595 |
|
|
case OFP_TABLE_FEATPROP_APPLY_SETFIELD: |
1596 |
|
|
case OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS: |
1597 |
|
|
while (tplen > 0) { |
1598 |
|
|
if ((oxm = ibuf_seek(ibuf, off, sizeof(*oxm))) == NULL) |
1599 |
|
|
return (-1); |
1600 |
|
|
|
1601 |
|
|
class = ntohs(oxm->oxm_class); |
1602 |
|
|
type = OFP_OXM_GET_FIELD(oxm); |
1603 |
|
|
length = oxm->oxm_length; |
1604 |
|
|
if (class == OFP_OXM_C_OPENFLOW_EXPERIMENTER) { |
1605 |
|
|
off += sizeof(*oxm) + 4; |
1606 |
|
|
tplen -= sizeof(*oxm) + 4; |
1607 |
|
|
} else { |
1608 |
|
|
off += sizeof(*oxm); |
1609 |
|
|
tplen -= sizeof(*oxm); |
1610 |
|
|
} |
1611 |
|
|
|
1612 |
|
|
log_debug("\t\t\tclass %s type %s length %d", |
1613 |
|
|
print_map(class, ofp_oxm_c_map), |
1614 |
|
|
print_map(type, ofp_xm_t_map), length); |
1615 |
|
|
} |
1616 |
|
|
break; |
1617 |
|
|
|
1618 |
|
|
case OFP_TABLE_FEATPROP_EXPERIMENTER: |
1619 |
|
|
case OFP_TABLE_FEATPROP_EXPERIMENTER_MISS: |
1620 |
|
|
off += tplen; |
1621 |
|
|
break; |
1622 |
|
|
|
1623 |
|
|
default: |
1624 |
|
|
return (-1); |
1625 |
|
|
} |
1626 |
|
|
|
1627 |
|
|
empty_table: |
1628 |
|
|
if (padsize) { |
1629 |
|
|
off += padsize; |
1630 |
|
|
remaining -= padsize; |
1631 |
|
|
hlen -= padsize; |
1632 |
|
|
} |
1633 |
|
|
if (hlen > 0) |
1634 |
|
|
goto next_property; |
1635 |
|
|
if (remaining > 0) |
1636 |
|
|
goto next_table; |
1637 |
|
|
|
1638 |
|
|
return (0); |
1639 |
|
|
} |
1640 |
|
|
|
1641 |
|
|
int |
1642 |
|
|
ofp13_multipart_reply_validate(struct switchd *sc, |
1643 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
1644 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
1645 |
|
|
{ |
1646 |
|
|
struct ofp_multipart *mp; |
1647 |
|
|
struct ofp_flow_stats *fs; |
1648 |
|
|
struct ofp_desc *d; |
1649 |
|
|
struct ofp_match *om; |
1650 |
|
|
struct ofp_ox_match *oxm; |
1651 |
|
|
struct ofp_instruction *oi; |
1652 |
|
|
int mptype, mpflags, hlen; |
1653 |
|
|
int remaining, matchlen, matchtype; |
1654 |
|
|
int ilen, padsize; |
1655 |
|
|
off_t off, moff, offdiff; |
1656 |
|
|
|
1657 |
|
|
remaining = ntohs(oh->oh_length); |
1658 |
|
|
|
1659 |
|
|
off = 0; |
1660 |
|
|
if ((mp = ibuf_seek(ibuf, off, sizeof(*mp))) == NULL) |
1661 |
|
|
return (-1); |
1662 |
|
|
|
1663 |
|
|
mptype = ntohs(mp->mp_type); |
1664 |
|
|
mpflags = ntohs(mp->mp_flags); |
1665 |
|
|
log_debug("\ttype %s flags %#04x", |
1666 |
|
|
print_map(mptype, ofp_mp_t_map), mpflags); |
1667 |
|
|
|
1668 |
|
|
off += sizeof(*mp); |
1669 |
|
|
remaining -= sizeof(*mp); |
1670 |
|
|
if (remaining == 0) { |
1671 |
|
|
log_debug("\tempty reply"); |
1672 |
|
|
return (0); |
1673 |
|
|
} |
1674 |
|
|
|
1675 |
|
|
switch (mptype) { |
1676 |
|
|
case OFP_MP_T_DESC: |
1677 |
|
|
if ((d = ibuf_seek(ibuf, off, sizeof(*d))) == NULL) |
1678 |
|
|
return (-1); |
1679 |
|
|
|
1680 |
|
|
off += sizeof(*d); |
1681 |
|
|
remaining -= sizeof(*d); |
1682 |
|
|
log_debug("\tmfr_desc \"%s\" hw_desc \"%s\" sw_desc \"%s\" " |
1683 |
|
|
"serial_num \"%s\" dp_desc \"%s\"", |
1684 |
|
|
d->d_mfr_desc, d->d_hw_desc, d->d_sw_desc, |
1685 |
|
|
d->d_serial_num, d->d_dp_desc); |
1686 |
|
|
break; |
1687 |
|
|
|
1688 |
|
|
case OFP_MP_T_FLOW: |
1689 |
|
|
read_next_flow: |
1690 |
|
|
if ((fs = ibuf_seek(ibuf, off, sizeof(*fs))) == NULL) |
1691 |
|
|
return (-1); |
1692 |
|
|
|
1693 |
|
|
hlen = ntohs(fs->fs_length); |
1694 |
|
|
remaining -= hlen; |
1695 |
|
|
moff = off + sizeof(*fs); |
1696 |
|
|
off += hlen; |
1697 |
|
|
|
1698 |
|
|
log_debug("\tflow length %d table_id %s duration sec %u " |
1699 |
|
|
"nsec %u prio %d timeout idle %d hard %d flags %#04x " |
1700 |
|
|
"cookie %#016llx packet count %llu byte count %llu", |
1701 |
|
|
hlen, print_map(fs->fs_table_id, ofp_table_id_map), |
1702 |
|
|
ntohl(fs->fs_duration_sec), ntohl(fs->fs_duration_nsec), |
1703 |
|
|
ntohs(fs->fs_priority), ntohs(fs->fs_idle_timeout), |
1704 |
|
|
ntohs(fs->fs_hard_timeout), ntohs(fs->fs_flags), |
1705 |
|
|
be64toh(fs->fs_cookie), be64toh(fs->fs_packet_count), |
1706 |
|
|
be64toh(fs->fs_byte_count)); |
1707 |
|
|
|
1708 |
|
|
om = &fs->fs_match; |
1709 |
|
|
matchtype = ntohs(om->om_type); |
1710 |
|
|
matchlen = ntohs(om->om_length); |
1711 |
|
|
padsize = OFP_ALIGN(matchlen) - matchlen; |
1712 |
|
|
ilen = hlen - |
1713 |
|
|
((sizeof(*fs) - sizeof(*om)) + matchlen + padsize); |
1714 |
|
|
|
1715 |
|
|
/* We don't know how to parse anything else yet. */ |
1716 |
|
|
if (matchtype != OFP_MATCH_OXM) |
1717 |
|
|
break; |
1718 |
|
|
|
1719 |
|
|
matchlen -= sizeof(*om); |
1720 |
|
|
while (matchlen) { |
1721 |
|
|
if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm))) == NULL) |
1722 |
|
|
return (-1); |
1723 |
|
|
if (ofp13_validate_oxm(sc, oxm, oh, ibuf, moff) == -1) |
1724 |
|
|
return (-1); |
1725 |
|
|
moff += sizeof(*oxm) + oxm->oxm_length; |
1726 |
|
|
matchlen -= sizeof(*oxm) + oxm->oxm_length; |
1727 |
|
|
} |
1728 |
|
|
|
1729 |
|
|
moff += padsize; |
1730 |
|
|
|
1731 |
|
|
while (ilen) { |
1732 |
|
|
offdiff = moff; |
1733 |
|
|
if ((oi = ibuf_seek(ibuf, moff, sizeof(*oi))) == NULL || |
1734 |
|
|
ofp13_validate_instruction(sc, oh, ibuf, |
1735 |
|
|
&moff, oi) == -1) |
1736 |
|
|
return (-1); |
1737 |
|
|
/* Avoid loops. */ |
1738 |
|
|
if ((moff - offdiff) == 0) |
1739 |
|
|
return (-1); |
1740 |
|
|
|
1741 |
|
|
ilen -= moff - offdiff; |
1742 |
|
|
} |
1743 |
|
|
|
1744 |
|
|
if (remaining) |
1745 |
|
|
goto read_next_flow; |
1746 |
|
|
break; |
1747 |
|
|
|
1748 |
|
|
case OFP_MP_T_AGGREGATE: |
1749 |
|
|
case OFP_MP_T_TABLE: |
1750 |
|
|
case OFP_MP_T_PORT_STATS: |
1751 |
|
|
case OFP_MP_T_QUEUE: |
1752 |
|
|
case OFP_MP_T_GROUP: |
1753 |
|
|
case OFP_MP_T_GROUP_DESC: |
1754 |
|
|
case OFP_MP_T_GROUP_FEATURES: |
1755 |
|
|
case OFP_MP_T_METER: |
1756 |
|
|
case OFP_MP_T_METER_CONFIG: |
1757 |
|
|
case OFP_MP_T_METER_FEATURES: |
1758 |
|
|
break; |
1759 |
|
|
|
1760 |
|
|
case OFP_MP_T_TABLE_FEATURES: |
1761 |
|
|
if (ofp13_validate_tableproperty(ibuf, off, remaining)) |
1762 |
|
|
return (-1); |
1763 |
|
|
break; |
1764 |
|
|
|
1765 |
|
|
case OFP_MP_T_PORT_DESC: |
1766 |
|
|
case OFP_MP_T_EXPERIMENTER: |
1767 |
|
|
break; |
1768 |
|
|
|
1769 |
|
|
default: |
1770 |
|
|
return (-1); |
1771 |
|
|
} |
1772 |
|
|
|
1773 |
|
|
return (0); |
1774 |
|
|
} |
1775 |
|
|
|
1776 |
|
|
/* Don't forget to update mp->mp_oh.oh_length */ |
1777 |
|
|
struct ofp_multipart * |
1778 |
|
|
ofp13_multipart_request(struct switch_connection *con, struct ibuf *ibuf, |
1779 |
|
|
uint16_t type, uint16_t flags) |
1780 |
|
|
{ |
1781 |
|
|
struct ofp_multipart *mp; |
1782 |
|
|
struct ofp_header *oh; |
1783 |
|
|
|
1784 |
|
|
if ((mp = ibuf_advance(ibuf, sizeof(*mp))) == NULL) |
1785 |
|
|
return (NULL); |
1786 |
|
|
|
1787 |
|
|
oh = &mp->mp_oh; |
1788 |
|
|
oh->oh_version = OFP_V_1_3; |
1789 |
|
|
oh->oh_type = OFP_T_MULTIPART_REQUEST; |
1790 |
|
|
oh->oh_xid = htonl(con->con_xidnxt++); |
1791 |
|
|
mp->mp_type = htons(type); |
1792 |
|
|
mp->mp_flags = htons(flags); |
1793 |
|
|
return (mp); |
1794 |
|
|
} |
1795 |
|
|
|
1796 |
|
|
int |
1797 |
|
|
ofp13_multipart_request_validate(struct switchd *sc, |
1798 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
1799 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
1800 |
|
|
{ |
1801 |
|
|
struct ofp_multipart *mp; |
1802 |
|
|
struct ofp_match *om; |
1803 |
|
|
struct ofp_flow_stats_request *fsr; |
1804 |
|
|
struct ofp_ox_match *oxm; |
1805 |
|
|
off_t off, moff; |
1806 |
|
|
int type, flags, totallen; |
1807 |
|
|
int matchlen, matchtype; |
1808 |
|
|
|
1809 |
|
|
off = 0; |
1810 |
|
|
if ((mp = ibuf_seek(ibuf, off, sizeof(*mp))) == NULL) |
1811 |
|
|
return (-1); |
1812 |
|
|
|
1813 |
|
|
type = ntohs(mp->mp_type); |
1814 |
|
|
flags = ntohs(mp->mp_flags); |
1815 |
|
|
log_debug("\ttype %s flags %#04x", |
1816 |
|
|
print_map(type, ofp_mp_t_map), flags); |
1817 |
|
|
|
1818 |
|
|
totallen = ntohs(oh->oh_length); |
1819 |
|
|
off += sizeof(*mp); |
1820 |
|
|
|
1821 |
|
|
switch (type) { |
1822 |
|
|
case OFP_MP_T_DESC: |
1823 |
|
|
/* This type doesn't use a payload. */ |
1824 |
|
|
if (totallen != sizeof(*mp)) |
1825 |
|
|
return (-1); |
1826 |
|
|
break; |
1827 |
|
|
|
1828 |
|
|
case OFP_MP_T_FLOW: |
1829 |
|
|
if ((fsr = ibuf_seek(ibuf, off, sizeof(*fsr))) == NULL) |
1830 |
|
|
return (-1); |
1831 |
|
|
|
1832 |
|
|
om = &fsr->fsr_match; |
1833 |
|
|
matchtype = ntohs(om->om_type); |
1834 |
|
|
matchlen = ntohs(om->om_length); |
1835 |
|
|
log_debug("\ttable_id %s out_port %s out_group %s " |
1836 |
|
|
"cookie %#016llx mask %#016llx match type %s length %d " |
1837 |
|
|
"(padded to %d)", |
1838 |
|
|
print_map(fsr->fsr_table_id, ofp_table_id_map), |
1839 |
|
|
print_map(ntohl(fsr->fsr_out_port), ofp_port_map), |
1840 |
|
|
print_map(ntohl(fsr->fsr_out_group), ofp_group_id_map), |
1841 |
|
|
be64toh(fsr->fsr_cookie), be64toh(fsr->fsr_cookie_mask), |
1842 |
|
|
print_map(matchtype, ofp_match_map), matchlen, |
1843 |
|
|
OFP_ALIGN(matchlen)); |
1844 |
|
|
|
1845 |
|
|
/* Get the first OXM byte offset. */ |
1846 |
|
|
moff = off + sizeof(*fsr); |
1847 |
|
|
|
1848 |
|
|
/* Don't count the header bytes. */ |
1849 |
|
|
matchlen -= sizeof(*om); |
1850 |
|
|
|
1851 |
|
|
/* We don't know how to parse anything else yet. */ |
1852 |
|
|
if (matchtype != OFP_MATCH_OXM) |
1853 |
|
|
break; |
1854 |
|
|
|
1855 |
|
|
while (matchlen) { |
1856 |
|
|
if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm))) == NULL) |
1857 |
|
|
return (-1); |
1858 |
|
|
if (ofp13_validate_oxm(sc, oxm, oh, ibuf, moff) == -1) |
1859 |
|
|
return (-1); |
1860 |
|
|
moff += sizeof(*oxm) + oxm->oxm_length; |
1861 |
|
|
matchlen -= sizeof(*oxm) + oxm->oxm_length; |
1862 |
|
|
} |
1863 |
|
|
break; |
1864 |
|
|
|
1865 |
|
|
case OFP_MP_T_AGGREGATE: |
1866 |
|
|
case OFP_MP_T_TABLE: |
1867 |
|
|
case OFP_MP_T_PORT_STATS: |
1868 |
|
|
case OFP_MP_T_QUEUE: |
1869 |
|
|
case OFP_MP_T_GROUP: |
1870 |
|
|
case OFP_MP_T_GROUP_DESC: |
1871 |
|
|
case OFP_MP_T_GROUP_FEATURES: |
1872 |
|
|
case OFP_MP_T_METER: |
1873 |
|
|
case OFP_MP_T_METER_CONFIG: |
1874 |
|
|
case OFP_MP_T_METER_FEATURES: |
1875 |
|
|
break; |
1876 |
|
|
|
1877 |
|
|
case OFP_MP_T_TABLE_FEATURES: |
1878 |
|
|
if (totallen == sizeof(*mp)) { |
1879 |
|
|
log_debug("\tempty table properties request"); |
1880 |
|
|
break; |
1881 |
|
|
} |
1882 |
|
|
break; |
1883 |
|
|
|
1884 |
|
|
case OFP_MP_T_PORT_DESC: |
1885 |
|
|
case OFP_MP_T_EXPERIMENTER: |
1886 |
|
|
break; |
1887 |
|
|
|
1888 |
|
|
default: |
1889 |
|
|
return (-1); |
1890 |
|
|
} |
1891 |
|
|
|
1892 |
|
|
return (0); |
1893 |
|
|
} |
1894 |
|
|
|
1895 |
|
|
int |
1896 |
|
|
ofp13_desc(struct switchd *sc, struct switch_connection *con) |
1897 |
|
|
{ |
1898 |
|
|
struct ofp_header *oh; |
1899 |
|
|
struct ofp_multipart *mp; |
1900 |
|
|
struct ibuf *ibuf; |
1901 |
|
|
int rv = -1; |
1902 |
|
|
|
1903 |
|
|
if ((ibuf = ibuf_static()) == NULL) |
1904 |
|
|
return (-1); |
1905 |
|
|
|
1906 |
|
|
if ((mp = ofp13_multipart_request(con, ibuf, OFP_MP_T_DESC, 0)) == NULL) |
1907 |
|
|
goto done; |
1908 |
|
|
|
1909 |
|
|
oh = &mp->mp_oh; |
1910 |
|
|
oh->oh_length = htons(sizeof(*mp)); |
1911 |
|
|
if (ofp13_validate(sc, &con->con_local, &con->con_peer, oh, ibuf) != 0) |
1912 |
|
|
goto done; |
1913 |
|
|
|
1914 |
|
|
rv = ofp_output(con, NULL, ibuf); |
1915 |
|
|
|
1916 |
|
|
done: |
1917 |
|
|
ibuf_free(ibuf); |
1918 |
|
|
return (rv); |
1919 |
|
|
} |
1920 |
|
|
|
1921 |
|
|
int |
1922 |
|
|
ofp13_flow_stats(struct switchd *sc, struct switch_connection *con, |
1923 |
|
|
uint32_t out_port, uint32_t out_group, uint8_t table_id) |
1924 |
|
|
{ |
1925 |
|
|
struct ofp_header *oh; |
1926 |
|
|
struct ofp_multipart *mp; |
1927 |
|
|
struct ofp_flow_stats_request *fsr; |
1928 |
|
|
struct ofp_match *om; |
1929 |
|
|
struct ibuf *ibuf; |
1930 |
|
|
int padsize, rv = -1; |
1931 |
|
|
|
1932 |
|
|
if ((ibuf = ibuf_static()) == NULL) |
1933 |
|
|
return (-1); |
1934 |
|
|
|
1935 |
|
|
if ((mp = ofp13_multipart_request(con, ibuf, OFP_MP_T_FLOW, 0)) == NULL) |
1936 |
|
|
goto done; |
1937 |
|
|
if ((fsr = ibuf_advance(ibuf, sizeof(*fsr))) == NULL) |
1938 |
|
|
goto done; |
1939 |
|
|
|
1940 |
|
|
oh = &mp->mp_oh; |
1941 |
|
|
fsr->fsr_table_id = table_id; |
1942 |
|
|
fsr->fsr_out_port = htonl(out_port); |
1943 |
|
|
fsr->fsr_out_group = htonl(out_group); |
1944 |
|
|
|
1945 |
|
|
om = &fsr->fsr_match; |
1946 |
|
|
om->om_type = htons(OFP_MATCH_OXM); |
1947 |
|
|
om->om_length = htons(sizeof(*om)); |
1948 |
|
|
padsize = OFP_ALIGN(sizeof(*om)) - sizeof(*om); |
1949 |
|
|
if (padsize && ibuf_advance(ibuf, padsize) == NULL) |
1950 |
|
|
goto done; |
1951 |
|
|
|
1952 |
|
|
oh->oh_length = htons(ibuf_length(ibuf)); |
1953 |
|
|
if (ofp13_validate(sc, &con->con_local, &con->con_peer, oh, ibuf) != 0) |
1954 |
|
|
goto done; |
1955 |
|
|
|
1956 |
|
|
rv = ofp_output(con, NULL, ibuf); |
1957 |
|
|
|
1958 |
|
|
done: |
1959 |
|
|
ibuf_free(ibuf); |
1960 |
|
|
return (rv); |
1961 |
|
|
} |
1962 |
|
|
|
1963 |
|
|
int |
1964 |
|
|
ofp13_table_features(struct switchd *sc, struct switch_connection *con, |
1965 |
|
|
uint8_t tableid) |
1966 |
|
|
{ |
1967 |
|
|
struct ofp_header *oh; |
1968 |
|
|
struct ofp_multipart *mp; |
1969 |
|
|
struct ibuf *ibuf; |
1970 |
|
|
int rv = -1; |
1971 |
|
|
|
1972 |
|
|
if ((ibuf = ibuf_static()) == NULL) |
1973 |
|
|
return (-1); |
1974 |
|
|
|
1975 |
|
|
if ((mp = ofp13_multipart_request(con, ibuf, |
1976 |
|
|
OFP_MP_T_TABLE_FEATURES, 0)) == NULL) |
1977 |
|
|
goto done; |
1978 |
|
|
|
1979 |
|
|
oh = &mp->mp_oh; |
1980 |
|
|
oh->oh_length = htons(sizeof(*mp)); |
1981 |
|
|
if (ofp13_validate(sc, &con->con_local, &con->con_peer, oh, ibuf) != 0) |
1982 |
|
|
goto done; |
1983 |
|
|
|
1984 |
|
|
rv = ofp_output(con, NULL, ibuf); |
1985 |
|
|
|
1986 |
|
|
done: |
1987 |
|
|
ibuf_free(ibuf); |
1988 |
|
|
return (rv); |
1989 |
|
|
} |
1990 |
|
|
|
1991 |
|
|
int |
1992 |
|
|
ofp13_error(struct switchd *sc, struct switch_connection *con, |
1993 |
|
|
struct ofp_header *oh, struct ibuf *ibuf, uint16_t type, uint16_t code) |
1994 |
|
|
{ |
1995 |
|
|
struct ibuf *obuf; |
1996 |
|
|
struct ofp_error *err; |
1997 |
|
|
struct ofp_header *header; |
1998 |
|
|
int dlen, rv = -1; |
1999 |
|
|
|
2000 |
|
|
if ((obuf = ibuf_static()) == NULL || |
2001 |
|
|
(err = ibuf_advance(obuf, sizeof(*err))) == NULL) |
2002 |
|
|
goto done; |
2003 |
|
|
|
2004 |
|
|
header = &err->err_oh; |
2005 |
|
|
err->err_type = htons(type); |
2006 |
|
|
err->err_code = htons(code); |
2007 |
|
|
|
2008 |
|
|
/* Copy the first message bytes for the error payload. */ |
2009 |
|
|
dlen = ibuf_size(ibuf); |
2010 |
|
|
if (dlen > OFP_ERRDATA_MAX) |
2011 |
|
|
dlen = OFP_ERRDATA_MAX; |
2012 |
|
|
if (ibuf_add(obuf, ibuf_seek(ibuf, 0, dlen), dlen) == -1) |
2013 |
|
|
goto done; |
2014 |
|
|
|
2015 |
|
|
header->oh_version = OFP_V_1_3; |
2016 |
|
|
header->oh_type = OFP_T_ERROR; |
2017 |
|
|
header->oh_length = htons(ibuf_length(obuf)); |
2018 |
|
|
header->oh_xid = oh->oh_xid; |
2019 |
|
|
if (ofp13_validate(sc, &con->con_peer, &con->con_local, header, |
2020 |
|
|
obuf) == -1) |
2021 |
|
|
goto done; |
2022 |
|
|
|
2023 |
|
|
rv = ofp_output(con, NULL, obuf); |
2024 |
|
|
|
2025 |
|
|
done: |
2026 |
|
|
ibuf_free(obuf); |
2027 |
|
|
return (rv); |
2028 |
|
|
} |
2029 |
|
|
|
2030 |
|
|
/* |
2031 |
|
|
* The valid commands for groups are: |
2032 |
|
|
* OFP_GROUPCMD_{ADD,MODIFY,DELETE} |
2033 |
|
|
* |
2034 |
|
|
* The valid type for groups are: |
2035 |
|
|
* OFP_GROUP_T_{ALL,SELECT,INDIRECT,FAST_FAILOVER} |
2036 |
|
|
* |
2037 |
|
|
* You have to update the gm->gm_oh.oh_length = htons(ibuf_length(ibuf)); |
2038 |
|
|
*/ |
2039 |
|
|
struct ofp_group_mod * |
2040 |
|
|
ofp13_group(struct switch_connection *con, struct ibuf *ibuf, |
2041 |
|
|
uint32_t gid, uint16_t cmd, uint8_t type) |
2042 |
|
|
{ |
2043 |
|
|
struct ofp_group_mod *gm; |
2044 |
|
|
struct ofp_header *oh; |
2045 |
|
|
|
2046 |
|
|
if ((gm = ibuf_advance(ibuf, sizeof(*gm))) == NULL) |
2047 |
|
|
return (NULL); |
2048 |
|
|
|
2049 |
|
|
oh = &gm->gm_oh; |
2050 |
|
|
oh->oh_version = OFP_V_1_3; |
2051 |
|
|
oh->oh_type = OFP_T_GROUP_MOD; |
2052 |
|
|
oh->oh_xid = htonl(con->con_xidnxt++); |
2053 |
|
|
gm->gm_command = htons(cmd); |
2054 |
|
|
gm->gm_type = type; |
2055 |
|
|
gm->gm_group_id = htonl(gid); |
2056 |
|
|
return (gm); |
2057 |
|
|
} |
2058 |
|
|
|
2059 |
|
|
/* Remember to update b->b_len. */ |
2060 |
|
|
struct ofp_bucket * |
2061 |
|
|
ofp13_bucket(struct ibuf *ibuf, uint16_t weight, uint32_t watchport, |
2062 |
|
|
uint32_t watchgroup) |
2063 |
|
|
{ |
2064 |
|
|
struct ofp_bucket *b; |
2065 |
|
|
|
2066 |
|
|
if ((b = ibuf_advance(ibuf, sizeof(*b))) == NULL) |
2067 |
|
|
return (NULL); |
2068 |
|
|
|
2069 |
|
|
b->b_weight = htons(weight); |
2070 |
|
|
b->b_watch_port = htonl(watchport); |
2071 |
|
|
b->b_watch_group = htonl(watchgroup); |
2072 |
|
|
return (b); |
2073 |
|
|
} |
2074 |
|
|
|
2075 |
|
|
int |
2076 |
|
|
ofp13_setconfig_validate(struct switchd *sc, |
2077 |
|
|
struct sockaddr_storage *src, struct sockaddr_storage *dst, |
2078 |
|
|
struct ofp_header *oh, struct ibuf *ibuf) |
2079 |
|
|
{ |
2080 |
|
|
struct ofp_switch_config *cfg; |
2081 |
|
|
|
2082 |
|
|
if ((cfg = ibuf_seek(ibuf, 0, sizeof(*cfg))) == NULL) |
2083 |
|
|
return (-1); |
2084 |
|
|
|
2085 |
|
|
log_debug("\tflags %#04x miss_send_len %s", |
2086 |
|
|
ntohs(cfg->cfg_flags), print_map(ntohs(cfg->cfg_miss_send_len), |
2087 |
|
|
ofp_controller_maxlen_map)); |
2088 |
|
|
return (0); |
2089 |
|
|
} |
2090 |
|
|
|
2091 |
|
|
int |
2092 |
|
|
ofp13_setconfig(struct switchd *sc, struct switch_connection *con, |
2093 |
|
|
uint16_t flags, uint16_t misslen) |
2094 |
|
|
{ |
2095 |
|
|
struct ibuf *ibuf; |
2096 |
|
|
struct ofp_switch_config *cfg; |
2097 |
|
|
struct ofp_header *oh; |
2098 |
|
|
int rv = -1; |
2099 |
|
|
|
2100 |
|
|
if ((ibuf = ibuf_static()) == NULL || |
2101 |
|
|
(cfg = ibuf_advance(ibuf, sizeof(*cfg))) == NULL) |
2102 |
|
|
goto done; |
2103 |
|
|
|
2104 |
|
|
cfg->cfg_flags = htons(flags); |
2105 |
|
|
cfg->cfg_miss_send_len = htons(misslen); |
2106 |
|
|
|
2107 |
|
|
oh = &cfg->cfg_oh; |
2108 |
|
|
oh->oh_version = OFP_V_1_3; |
2109 |
|
|
oh->oh_type = OFP_T_SET_CONFIG; |
2110 |
|
|
oh->oh_length = htons(ibuf_length(ibuf)); |
2111 |
|
|
oh->oh_xid = htonl(con->con_xidnxt++); |
2112 |
|
|
if (ofp13_validate(sc, &con->con_local, &con->con_peer, oh, ibuf) != 0) |
2113 |
|
|
goto done; |
2114 |
|
|
|
2115 |
|
|
rv = ofp_output(con, NULL, ibuf); |
2116 |
|
|
|
2117 |
|
|
done: |
2118 |
|
|
ibuf_free(ibuf); |
2119 |
|
|
return (rv); |
2120 |
|
|
} |
2121 |
|
|
|
2122 |
|
|
/* |
2123 |
|
|
* Flow modification message. |
2124 |
|
|
* |
2125 |
|
|
* After the flow-mod header we have N OXM filters to match packets, when |
2126 |
|
|
* you finish adding them you must update match header: |
2127 |
|
|
* fm_match.om_length = sizeof(fm_match) + OXM length. |
2128 |
|
|
* |
2129 |
|
|
* Then you must add flow instructions and update the OFP header length: |
2130 |
|
|
* fm_oh.oh_length = |
2131 |
|
|
* sizeof(*fm) + (fm_match.om_len - sizeof(fm_match)) + instructionslen. |
2132 |
|
|
* or |
2133 |
|
|
* fm_oh.oh_length = ibuf_length(ibuf). |
2134 |
|
|
* |
2135 |
|
|
* Note on match payload: |
2136 |
|
|
* After adding all matches and before starting to insert instructions you |
2137 |
|
|
* must add the mandatory padding to fm_match. You can calculate the padding |
2138 |
|
|
* size with this formula: |
2139 |
|
|
* padsize = OFP_ALIGN(fm_match.om_length) - fm_match.om_length; |
2140 |
|
|
* |
2141 |
|
|
* Note on Table-miss: |
2142 |
|
|
* To make a table miss you need to set priority 0 and don't add any |
2143 |
|
|
* matches, just instructions. |
2144 |
|
|
*/ |
2145 |
|
|
struct ofp_flow_mod * |
2146 |
|
|
ofp13_flowmod(struct switch_connection *con, struct ibuf *ibuf, |
2147 |
|
|
uint8_t cmd, uint8_t table, uint16_t idleto, uint16_t hardto, |
2148 |
|
|
uint16_t prio) |
2149 |
|
|
{ |
2150 |
|
|
struct ofp_flow_mod *fm; |
2151 |
|
|
|
2152 |
|
|
if ((fm = ibuf_advance(ibuf, sizeof(*fm))) == NULL) |
2153 |
|
|
return (NULL); |
2154 |
|
|
|
2155 |
|
|
fm->fm_oh.oh_version = OFP_V_1_3; |
2156 |
|
|
fm->fm_oh.oh_type = OFP_T_FLOW_MOD; |
2157 |
|
|
fm->fm_oh.oh_length = htons(sizeof(*fm)); |
2158 |
|
|
fm->fm_oh.oh_xid = htonl(con->con_xidnxt++); |
2159 |
|
|
|
2160 |
|
|
fm->fm_command = cmd; |
2161 |
|
|
fm->fm_buffer_id = htonl(OFP_PKTOUT_NO_BUFFER); |
2162 |
|
|
fm->fm_flags = htons(OFP_FLOWFLAG_SEND_FLOW_REMOVED); |
2163 |
|
|
|
2164 |
|
|
fm->fm_match.om_type = htons(OFP_MATCH_OXM); |
2165 |
|
|
fm->fm_match.om_length = htons(sizeof(fm->fm_match)); |
2166 |
|
|
|
2167 |
|
|
return (fm); |
2168 |
|
|
} |
2169 |
|
|
|
2170 |
|
|
int |
2171 |
|
|
ofp13_tablemiss_sendctrl(struct switchd *sc, struct switch_connection *con, |
2172 |
|
|
uint8_t table) |
2173 |
|
|
{ |
2174 |
|
|
struct oflowmod_ctx ctx; |
2175 |
|
|
struct ibuf *ibuf; |
2176 |
|
|
int ret; |
2177 |
|
|
|
2178 |
|
|
if ((ibuf = oflowmod_open(&ctx, con, NULL, OFP_V_1_3)) == NULL) |
2179 |
|
|
goto err; |
2180 |
|
|
|
2181 |
|
|
if (oflowmod_iopen(&ctx) == -1) |
2182 |
|
|
goto err; |
2183 |
|
|
|
2184 |
|
|
/* Update header */ |
2185 |
|
|
ctx.ctx_fm->fm_table_id = table; |
2186 |
|
|
|
2187 |
|
|
if (oflowmod_instruction(&ctx, |
2188 |
|
|
OFP_INSTRUCTION_T_APPLY_ACTIONS) == -1) |
2189 |
|
|
goto err; |
2190 |
|
|
if (action_output(ibuf, OFP_PORT_CONTROLLER, |
2191 |
|
|
OFP_CONTROLLER_MAXLEN_MAX) == -1) |
2192 |
|
|
goto err; |
2193 |
|
|
|
2194 |
|
|
if (oflowmod_iclose(&ctx) == -1) |
2195 |
|
|
goto err; |
2196 |
|
|
if (oflowmod_close(&ctx) == -1) |
2197 |
|
|
goto err; |
2198 |
|
|
|
2199 |
|
|
if (ofp13_validate(sc, &con->con_local, &con->con_peer, |
2200 |
|
|
&ctx.ctx_fm->fm_oh, ibuf) != 0) |
2201 |
|
|
goto err; |
2202 |
|
|
|
2203 |
|
|
ret = ofp_output(con, NULL, ibuf); |
2204 |
|
|
ibuf_release(ibuf); |
2205 |
|
|
|
2206 |
|
|
return (ret); |
2207 |
|
|
|
2208 |
|
|
err: |
2209 |
|
|
(void)oflowmod_err(&ctx, __func__, __LINE__); |
2210 |
|
|
return (-1); |
2211 |
|
|
} |
2212 |
|
|
|
2213 |
|
|
int |
2214 |
|
|
ofp13_switchconfigure(struct switchd *sc, struct switch_connection *con) |
2215 |
|
|
{ |
2216 |
|
|
struct switch_table *st; |
2217 |
|
|
|
2218 |
|
|
/* Look for a table with 'apply' and 'output' support for miss. */ |
2219 |
|
|
TAILQ_FOREACH(st, &con->con_stlist, st_entry) { |
2220 |
|
|
if ((st->st_instructionsmiss & |
2221 |
|
|
(1ULL << OFP_INSTRUCTION_T_APPLY_ACTIONS)) == 0) |
2222 |
|
|
continue; |
2223 |
|
|
if ((st->st_actionsmiss & (1ULL << OFP_ACTION_OUTPUT)) == 0) |
2224 |
|
|
continue; |
2225 |
|
|
|
2226 |
|
|
break; |
2227 |
|
|
} |
2228 |
|
|
if (st == NULL) { |
2229 |
|
|
log_warn("No apply output for this switch"); |
2230 |
|
|
return (-1); |
2231 |
|
|
} |
2232 |
|
|
|
2233 |
|
|
/* Install the flow to receive packets from the switch. */ |
2234 |
|
|
return (ofp13_tablemiss_sendctrl(sc, con, st->st_table)); |
2235 |
|
|
} |
2236 |
|
|
|
2237 |
|
|
int |
2238 |
|
|
ofp13_getflowtable(struct switch_connection *con) |
2239 |
|
|
{ |
2240 |
|
|
struct switch_table *st; |
2241 |
|
|
|
2242 |
|
|
/* Look for a table with 'apply' and 'output' support. */ |
2243 |
|
|
TAILQ_FOREACH(st, &con->con_stlist, st_entry) { |
2244 |
|
|
if ((st->st_instructions & |
2245 |
|
|
(1ULL << OFP_INSTRUCTION_T_APPLY_ACTIONS)) == 0) |
2246 |
|
|
continue; |
2247 |
|
|
if ((st->st_actions & (1ULL << OFP_ACTION_OUTPUT)) == 0) |
2248 |
|
|
continue; |
2249 |
|
|
|
2250 |
|
|
break; |
2251 |
|
|
} |
2252 |
|
|
if (st == NULL) |
2253 |
|
|
return (-1); |
2254 |
|
|
|
2255 |
|
|
return (st->st_table); |
2256 |
|
|
} |