]>
Commit | Line | Data |
---|---|---|
0d71302e BP |
1 | /* |
2 | * Copyright (c) 2008-2017 Nicira, Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | #include "openvswitch/ofp-switch.h" | |
19 | #include "byte-order.h" | |
20 | #include "openvswitch/ofpbuf.h" | |
21 | #include "openvswitch/ofp-actions.h" | |
22 | #include "openvswitch/ofp-errors.h" | |
23 | #include "openvswitch/ofp-msgs.h" | |
24 | #include "openvswitch/ofp-port.h" | |
dfc77282 | 25 | #include "openvswitch/ofp-print.h" |
0d71302e BP |
26 | #include "util.h" |
27 | ||
28 | /* ofputil_switch_features */ | |
29 | ||
30 | #define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \ | |
31 | OFPC_IP_REASM | OFPC_QUEUE_STATS) | |
32 | BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS); | |
33 | BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS); | |
34 | BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS); | |
35 | BUILD_ASSERT_DECL((int) OFPUTIL_C_IP_REASM == OFPC_IP_REASM); | |
36 | BUILD_ASSERT_DECL((int) OFPUTIL_C_QUEUE_STATS == OFPC_QUEUE_STATS); | |
37 | BUILD_ASSERT_DECL((int) OFPUTIL_C_ARP_MATCH_IP == OFPC_ARP_MATCH_IP); | |
38 | BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_BLOCKED == OFPC12_PORT_BLOCKED); | |
39 | BUILD_ASSERT_DECL((int) OFPUTIL_C_BUNDLES == OFPC14_BUNDLES); | |
40 | BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_MONITORING == OFPC14_FLOW_MONITORING); | |
41 | ||
42 | static uint32_t | |
43 | ofputil_capabilities_mask(enum ofp_version ofp_version) | |
44 | { | |
45 | /* Handle capabilities whose bit is unique for all OpenFlow versions */ | |
46 | switch (ofp_version) { | |
47 | case OFP10_VERSION: | |
48 | case OFP11_VERSION: | |
49 | return OFPC_COMMON | OFPC_ARP_MATCH_IP; | |
50 | case OFP12_VERSION: | |
51 | case OFP13_VERSION: | |
52 | return OFPC_COMMON | OFPC12_PORT_BLOCKED; | |
53 | case OFP14_VERSION: | |
54 | case OFP15_VERSION: | |
0d71302e BP |
55 | return OFPC_COMMON | OFPC12_PORT_BLOCKED | OFPC14_BUNDLES |
56 | | OFPC14_FLOW_MONITORING; | |
57 | default: | |
58 | /* Caller needs to check osf->header.version itself */ | |
59 | return 0; | |
60 | } | |
61 | } | |
62 | ||
63 | /* Pulls an OpenFlow "switch_features" structure from 'b' and decodes it into | |
64 | * an abstract representation in '*features', readying 'b' to iterate over the | |
65 | * OpenFlow port structures following 'osf' with later calls to | |
66 | * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an OFPERR_* | |
67 | * value. */ | |
68 | enum ofperr | |
69 | ofputil_pull_switch_features(struct ofpbuf *b, | |
70 | struct ofputil_switch_features *features) | |
71 | { | |
72 | const struct ofp_header *oh = b->data; | |
73 | enum ofpraw raw = ofpraw_pull_assert(b); | |
74 | const struct ofp_switch_features *osf = ofpbuf_pull(b, sizeof *osf); | |
75 | features->datapath_id = ntohll(osf->datapath_id); | |
76 | features->n_buffers = ntohl(osf->n_buffers); | |
77 | features->n_tables = osf->n_tables; | |
78 | features->auxiliary_id = 0; | |
79 | ||
80 | features->capabilities = ntohl(osf->capabilities) & | |
81 | ofputil_capabilities_mask(oh->version); | |
82 | ||
83 | if (raw == OFPRAW_OFPT10_FEATURES_REPLY) { | |
84 | if (osf->capabilities & htonl(OFPC10_STP)) { | |
85 | features->capabilities |= OFPUTIL_C_STP; | |
86 | } | |
87 | features->ofpacts = ofpact_bitmap_from_openflow(osf->actions, | |
88 | OFP10_VERSION); | |
89 | } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY | |
90 | || raw == OFPRAW_OFPT13_FEATURES_REPLY) { | |
91 | if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) { | |
92 | features->capabilities |= OFPUTIL_C_GROUP_STATS; | |
93 | } | |
94 | features->ofpacts = 0; | |
95 | if (raw == OFPRAW_OFPT13_FEATURES_REPLY) { | |
96 | features->auxiliary_id = osf->auxiliary_id; | |
97 | } | |
98 | } else { | |
99 | return OFPERR_OFPBRC_BAD_VERSION; | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | /* In OpenFlow 1.0, 1.1, and 1.2, an OFPT_FEATURES_REPLY message lists all the | |
106 | * switch's ports, unless there are too many to fit. In OpenFlow 1.3 and | |
107 | * later, an OFPT_FEATURES_REPLY does not list ports at all. | |
108 | * | |
109 | * Given a buffer 'b' that contains a Features Reply message, this message | |
110 | * checks if it contains a complete list of the switch's ports. Returns true, | |
111 | * if so. Returns false if the list is missing (OF1.3+) or incomplete | |
112 | * (OF1.0/1.1/1.2), and in the latter case removes all of the ports from the | |
113 | * message. | |
114 | * | |
115 | * When this function returns false, the caller should send an OFPST_PORT_DESC | |
116 | * stats request to get the ports. */ | |
117 | bool | |
118 | ofputil_switch_features_has_ports(struct ofpbuf *b) | |
119 | { | |
120 | struct ofp_header *oh = b->data; | |
121 | size_t phy_port_size; | |
122 | ||
123 | if (oh->version >= OFP13_VERSION) { | |
124 | /* OpenFlow 1.3+ never has ports in the feature reply. */ | |
125 | return false; | |
126 | } | |
127 | ||
128 | phy_port_size = (oh->version == OFP10_VERSION | |
129 | ? sizeof(struct ofp10_phy_port) | |
130 | : sizeof(struct ofp11_port)); | |
131 | if (ntohs(oh->length) + phy_port_size <= UINT16_MAX) { | |
132 | /* There's room for additional ports in the feature reply. | |
133 | * Assume that the list is complete. */ | |
134 | return true; | |
135 | } | |
136 | ||
137 | /* The feature reply has no room for more ports. Probably the list is | |
138 | * truncated. Drop the ports and tell the caller to retrieve them with | |
139 | * OFPST_PORT_DESC. */ | |
140 | b->size = sizeof *oh + sizeof(struct ofp_switch_features); | |
141 | ofpmsg_update_length(b); | |
142 | return false; | |
143 | } | |
144 | ||
145 | /* Returns a buffer owned by the caller that encodes 'features' in the format | |
146 | * required by 'protocol' with the given 'xid'. The caller should append port | |
147 | * information to the buffer with subsequent calls to | |
148 | * ofputil_put_switch_features_port(). */ | |
149 | struct ofpbuf * | |
150 | ofputil_encode_switch_features(const struct ofputil_switch_features *features, | |
151 | enum ofputil_protocol protocol, ovs_be32 xid) | |
152 | { | |
153 | struct ofp_switch_features *osf; | |
154 | struct ofpbuf *b; | |
155 | enum ofp_version version; | |
156 | enum ofpraw raw; | |
157 | ||
158 | version = ofputil_protocol_to_ofp_version(protocol); | |
159 | switch (version) { | |
160 | case OFP10_VERSION: | |
161 | raw = OFPRAW_OFPT10_FEATURES_REPLY; | |
162 | break; | |
163 | case OFP11_VERSION: | |
164 | case OFP12_VERSION: | |
165 | raw = OFPRAW_OFPT11_FEATURES_REPLY; | |
166 | break; | |
167 | case OFP13_VERSION: | |
168 | case OFP14_VERSION: | |
169 | case OFP15_VERSION: | |
0d71302e BP |
170 | raw = OFPRAW_OFPT13_FEATURES_REPLY; |
171 | break; | |
172 | default: | |
173 | OVS_NOT_REACHED(); | |
174 | } | |
175 | b = ofpraw_alloc_xid(raw, version, xid, 0); | |
176 | osf = ofpbuf_put_zeros(b, sizeof *osf); | |
177 | osf->datapath_id = htonll(features->datapath_id); | |
178 | osf->n_buffers = htonl(features->n_buffers); | |
179 | osf->n_tables = features->n_tables; | |
180 | ||
181 | osf->capabilities = htonl(features->capabilities & | |
182 | ofputil_capabilities_mask(version)); | |
183 | switch (version) { | |
184 | case OFP10_VERSION: | |
185 | if (features->capabilities & OFPUTIL_C_STP) { | |
186 | osf->capabilities |= htonl(OFPC10_STP); | |
187 | } | |
188 | osf->actions = ofpact_bitmap_to_openflow(features->ofpacts, | |
189 | OFP10_VERSION); | |
190 | break; | |
191 | case OFP13_VERSION: | |
192 | case OFP14_VERSION: | |
193 | case OFP15_VERSION: | |
0d71302e BP |
194 | osf->auxiliary_id = features->auxiliary_id; |
195 | /* fall through */ | |
196 | case OFP11_VERSION: | |
197 | case OFP12_VERSION: | |
198 | if (features->capabilities & OFPUTIL_C_GROUP_STATS) { | |
199 | osf->capabilities |= htonl(OFPC11_GROUP_STATS); | |
200 | } | |
201 | break; | |
202 | default: | |
203 | OVS_NOT_REACHED(); | |
204 | } | |
205 | ||
206 | return b; | |
207 | } | |
208 | ||
209 | /* Encodes 'pp' into the format required by the switch_features message already | |
210 | * in 'b', which should have been returned by ofputil_encode_switch_features(), | |
211 | * and appends the encoded version to 'b'. */ | |
212 | void | |
213 | ofputil_put_switch_features_port(const struct ofputil_phy_port *pp, | |
214 | struct ofpbuf *b) | |
215 | { | |
216 | const struct ofp_header *oh = b->data; | |
217 | ||
218 | if (oh->version < OFP13_VERSION) { | |
219 | /* Try adding a port description to the message, but drop it again if | |
220 | * the buffer overflows. (This possibility for overflow is why | |
221 | * OpenFlow 1.3+ moved port descriptions into a multipart message.) */ | |
222 | size_t start_ofs = b->size; | |
223 | ofputil_put_phy_port(oh->version, pp, b); | |
224 | if (b->size > UINT16_MAX) { | |
225 | b->size = start_ofs; | |
226 | } | |
227 | } | |
228 | } | |
229 | ||
dfc77282 BP |
230 | static const char * |
231 | ofputil_capabilities_to_name(uint32_t bit) | |
232 | { | |
233 | enum ofputil_capabilities capabilities = bit; | |
234 | ||
235 | switch (capabilities) { | |
236 | case OFPUTIL_C_FLOW_STATS: return "FLOW_STATS"; | |
237 | case OFPUTIL_C_TABLE_STATS: return "TABLE_STATS"; | |
238 | case OFPUTIL_C_PORT_STATS: return "PORT_STATS"; | |
239 | case OFPUTIL_C_IP_REASM: return "IP_REASM"; | |
240 | case OFPUTIL_C_QUEUE_STATS: return "QUEUE_STATS"; | |
241 | case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP"; | |
242 | case OFPUTIL_C_STP: return "STP"; | |
243 | case OFPUTIL_C_GROUP_STATS: return "GROUP_STATS"; | |
244 | case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED"; | |
245 | case OFPUTIL_C_BUNDLES: return "BUNDLES"; | |
246 | case OFPUTIL_C_FLOW_MONITORING: return "FLOW_MONITORING"; | |
247 | } | |
248 | ||
249 | return NULL; | |
250 | } | |
251 | ||
252 | void | |
253 | ofputil_switch_features_format(struct ds *s, | |
254 | const struct ofputil_switch_features *features) | |
255 | { | |
256 | ds_put_format(s, " dpid:%016"PRIx64"\n", features->datapath_id); | |
257 | ||
258 | ds_put_format(s, "n_tables:%"PRIu8", n_buffers:%"PRIu32, | |
259 | features->n_tables, features->n_buffers); | |
260 | if (features->auxiliary_id) { | |
261 | ds_put_format(s, ", auxiliary_id:%"PRIu8, features->auxiliary_id); | |
262 | } | |
263 | ds_put_char(s, '\n'); | |
264 | ||
265 | ds_put_cstr(s, "capabilities: "); | |
266 | ofp_print_bit_names(s, features->capabilities, | |
267 | ofputil_capabilities_to_name, ' '); | |
268 | ds_put_char(s, '\n'); | |
269 | ||
270 | if (features->ofpacts) { | |
271 | ds_put_cstr(s, "actions: "); | |
272 | ofpact_bitmap_format(features->ofpacts, s); | |
273 | ds_put_char(s, '\n'); | |
274 | } | |
275 | } | |
276 | ||
0d71302e BP |
277 | const char * |
278 | ofputil_frag_handling_to_string(enum ofputil_frag_handling frag) | |
279 | { | |
280 | switch (frag) { | |
281 | case OFPUTIL_FRAG_NORMAL: return "normal"; | |
282 | case OFPUTIL_FRAG_DROP: return "drop"; | |
283 | case OFPUTIL_FRAG_REASM: return "reassemble"; | |
284 | case OFPUTIL_FRAG_NX_MATCH: return "nx-match"; | |
285 | } | |
286 | ||
287 | OVS_NOT_REACHED(); | |
288 | } | |
289 | ||
290 | bool | |
291 | ofputil_frag_handling_from_string(const char *s, | |
292 | enum ofputil_frag_handling *frag) | |
293 | { | |
294 | if (!strcasecmp(s, "normal")) { | |
295 | *frag = OFPUTIL_FRAG_NORMAL; | |
296 | } else if (!strcasecmp(s, "drop")) { | |
297 | *frag = OFPUTIL_FRAG_DROP; | |
298 | } else if (!strcasecmp(s, "reassemble")) { | |
299 | *frag = OFPUTIL_FRAG_REASM; | |
300 | } else if (!strcasecmp(s, "nx-match")) { | |
301 | *frag = OFPUTIL_FRAG_NX_MATCH; | |
302 | } else { | |
303 | return false; | |
304 | } | |
305 | return true; | |
306 | } | |
307 | \f | |
308 | /* ofputil_switch_config */ | |
309 | ||
310 | /* Decodes 'oh', which must be an OFPT_GET_CONFIG_REPLY or OFPT_SET_CONFIG | |
311 | * message, into 'config'. Returns false if 'oh' contained any flags that | |
312 | * aren't specified in its version of OpenFlow, true otherwise. */ | |
313 | static bool | |
314 | ofputil_decode_switch_config(const struct ofp_header *oh, | |
315 | struct ofputil_switch_config *config) | |
316 | { | |
317 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
318 | ofpraw_pull_assert(&b); | |
319 | ||
320 | const struct ofp_switch_config *osc = ofpbuf_pull(&b, sizeof *osc); | |
321 | config->frag = ntohs(osc->flags) & OFPC_FRAG_MASK; | |
322 | config->miss_send_len = ntohs(osc->miss_send_len); | |
323 | ||
324 | ovs_be16 valid_mask = htons(OFPC_FRAG_MASK); | |
325 | if (oh->version < OFP13_VERSION) { | |
326 | const ovs_be16 ttl_bit = htons(OFPC_INVALID_TTL_TO_CONTROLLER); | |
327 | valid_mask |= ttl_bit; | |
328 | config->invalid_ttl_to_controller = (osc->flags & ttl_bit) != 0; | |
329 | } else { | |
330 | config->invalid_ttl_to_controller = -1; | |
331 | } | |
332 | ||
333 | return !(osc->flags & ~valid_mask); | |
334 | } | |
335 | ||
336 | void | |
337 | ofputil_decode_get_config_reply(const struct ofp_header *oh, | |
338 | struct ofputil_switch_config *config) | |
339 | { | |
340 | ofputil_decode_switch_config(oh, config); | |
341 | } | |
342 | ||
343 | enum ofperr | |
344 | ofputil_decode_set_config(const struct ofp_header *oh, | |
345 | struct ofputil_switch_config *config) | |
346 | { | |
347 | return (ofputil_decode_switch_config(oh, config) | |
348 | ? 0 | |
349 | : OFPERR_OFPSCFC_BAD_FLAGS); | |
350 | } | |
351 | ||
352 | static struct ofpbuf * | |
353 | ofputil_put_switch_config(const struct ofputil_switch_config *config, | |
354 | struct ofpbuf *b) | |
355 | { | |
356 | const struct ofp_header *oh = b->data; | |
357 | struct ofp_switch_config *osc = ofpbuf_put_zeros(b, sizeof *osc); | |
358 | osc->flags = htons(config->frag); | |
359 | if (config->invalid_ttl_to_controller > 0 && oh->version < OFP13_VERSION) { | |
360 | osc->flags |= htons(OFPC_INVALID_TTL_TO_CONTROLLER); | |
361 | } | |
362 | osc->miss_send_len = htons(config->miss_send_len); | |
363 | return b; | |
364 | } | |
365 | ||
366 | struct ofpbuf * | |
367 | ofputil_encode_get_config_reply(const struct ofp_header *request, | |
368 | const struct ofputil_switch_config *config) | |
369 | { | |
370 | struct ofpbuf *b = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY, | |
371 | request, 0); | |
372 | return ofputil_put_switch_config(config, b); | |
373 | } | |
374 | ||
375 | struct ofpbuf * | |
376 | ofputil_encode_set_config(const struct ofputil_switch_config *config, | |
377 | enum ofp_version version) | |
378 | { | |
379 | struct ofpbuf *b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, version, 0); | |
380 | return ofputil_put_switch_config(config, b); | |
381 | } | |
dfc77282 BP |
382 | |
383 | void | |
384 | ofputil_switch_config_format(struct ds *s, | |
385 | const struct ofputil_switch_config *config) | |
386 | { | |
387 | ds_put_format(s, " frags=%s", | |
388 | ofputil_frag_handling_to_string(config->frag)); | |
389 | ||
390 | if (config->invalid_ttl_to_controller > 0) { | |
391 | ds_put_format(s, " invalid_ttl_to_controller"); | |
392 | } | |
393 | ||
394 | ds_put_format(s, " miss_send_len=%"PRIu16"\n", config->miss_send_len); | |
395 | } |