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