/src/openvswitch/lib/ofp-switch.c
Line | Count | Source (jump to first uncovered line) |
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 | 5.11k | #define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \ |
31 | 5.11k | 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 | 5.11k | { |
45 | | /* Handle capabilities whose bit is unique for all OpenFlow versions */ |
46 | 5.11k | switch (ofp_version) { |
47 | 2.92k | case OFP10_VERSION: |
48 | 3.00k | case OFP11_VERSION: |
49 | 3.00k | return OFPC_COMMON | OFPC_ARP_MATCH_IP; |
50 | 829 | case OFP12_VERSION: |
51 | 1.12k | case OFP13_VERSION: |
52 | 1.12k | return OFPC_COMMON | OFPC12_PORT_BLOCKED; |
53 | 90 | case OFP14_VERSION: |
54 | 991 | case OFP15_VERSION: |
55 | 991 | return OFPC_COMMON | OFPC12_PORT_BLOCKED | OFPC14_BUNDLES |
56 | 991 | | OFPC14_FLOW_MONITORING; |
57 | 0 | default: |
58 | | /* Caller needs to check osf->header.version itself */ |
59 | 0 | return 0; |
60 | 5.11k | } |
61 | 5.11k | } |
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 | 5.11k | { |
72 | 5.11k | const struct ofp_header *oh = b->data; |
73 | 5.11k | enum ofpraw raw = ofpraw_pull_assert(b); |
74 | 5.11k | const struct ofp_switch_features *osf = ofpbuf_pull(b, sizeof *osf); |
75 | 5.11k | features->datapath_id = ntohll(osf->datapath_id); |
76 | 5.11k | features->n_buffers = ntohl(osf->n_buffers); |
77 | 5.11k | features->n_tables = osf->n_tables; |
78 | 5.11k | features->auxiliary_id = 0; |
79 | | |
80 | 5.11k | features->capabilities = ntohl(osf->capabilities) & |
81 | 5.11k | ofputil_capabilities_mask(oh->version); |
82 | | |
83 | 5.11k | if (raw == OFPRAW_OFPT10_FEATURES_REPLY) { |
84 | 2.92k | if (osf->capabilities & htonl(OFPC10_STP)) { |
85 | 1.16k | features->capabilities |= OFPUTIL_C_STP; |
86 | 1.16k | } |
87 | 2.92k | features->ofpacts = ofpact_bitmap_from_openflow(osf->actions, |
88 | 2.92k | OFP10_VERSION); |
89 | 2.92k | } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY |
90 | 2.19k | || raw == OFPRAW_OFPT13_FEATURES_REPLY) { |
91 | 2.19k | if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) { |
92 | 773 | features->capabilities |= OFPUTIL_C_GROUP_STATS; |
93 | 773 | } |
94 | 2.19k | features->ofpacts = 0; |
95 | 2.19k | if (raw == OFPRAW_OFPT13_FEATURES_REPLY) { |
96 | 1.28k | features->auxiliary_id = osf->auxiliary_id; |
97 | 1.28k | } |
98 | 2.19k | } else { |
99 | 0 | return OFPERR_OFPBRC_BAD_VERSION; |
100 | 0 | } |
101 | | |
102 | 5.11k | return 0; |
103 | 5.11k | } |
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 | 0 | { |
120 | 0 | struct ofp_header *oh = b->data; |
121 | 0 | size_t phy_port_size; |
122 | |
|
123 | 0 | if (oh->version >= OFP13_VERSION) { |
124 | | /* OpenFlow 1.3+ never has ports in the feature reply. */ |
125 | 0 | return false; |
126 | 0 | } |
127 | | |
128 | 0 | phy_port_size = (oh->version == OFP10_VERSION |
129 | 0 | ? sizeof(struct ofp10_phy_port) |
130 | 0 | : sizeof(struct ofp11_port)); |
131 | 0 | 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 | 0 | return true; |
135 | 0 | } |
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 | 0 | b->size = sizeof *oh + sizeof(struct ofp_switch_features); |
141 | 0 | ofpmsg_update_length(b); |
142 | 0 | return false; |
143 | 0 | } |
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 | 0 | { |
153 | 0 | struct ofp_switch_features *osf; |
154 | 0 | struct ofpbuf *b; |
155 | 0 | enum ofp_version version; |
156 | 0 | enum ofpraw raw; |
157 | |
|
158 | 0 | version = ofputil_protocol_to_ofp_version(protocol); |
159 | 0 | switch (version) { |
160 | 0 | case OFP10_VERSION: |
161 | 0 | raw = OFPRAW_OFPT10_FEATURES_REPLY; |
162 | 0 | break; |
163 | 0 | case OFP11_VERSION: |
164 | 0 | case OFP12_VERSION: |
165 | 0 | raw = OFPRAW_OFPT11_FEATURES_REPLY; |
166 | 0 | break; |
167 | 0 | case OFP13_VERSION: |
168 | 0 | case OFP14_VERSION: |
169 | 0 | case OFP15_VERSION: |
170 | 0 | raw = OFPRAW_OFPT13_FEATURES_REPLY; |
171 | 0 | break; |
172 | 0 | default: |
173 | 0 | OVS_NOT_REACHED(); |
174 | 0 | } |
175 | 0 | b = ofpraw_alloc_xid(raw, version, xid, 0); |
176 | 0 | osf = ofpbuf_put_zeros(b, sizeof *osf); |
177 | 0 | osf->datapath_id = htonll(features->datapath_id); |
178 | 0 | osf->n_buffers = htonl(features->n_buffers); |
179 | 0 | osf->n_tables = features->n_tables; |
180 | |
|
181 | 0 | osf->capabilities = htonl(features->capabilities & |
182 | 0 | ofputil_capabilities_mask(version)); |
183 | 0 | switch (version) { |
184 | 0 | case OFP10_VERSION: |
185 | 0 | if (features->capabilities & OFPUTIL_C_STP) { |
186 | 0 | osf->capabilities |= htonl(OFPC10_STP); |
187 | 0 | } |
188 | 0 | osf->actions = ofpact_bitmap_to_openflow(features->ofpacts, |
189 | 0 | OFP10_VERSION); |
190 | 0 | break; |
191 | 0 | case OFP13_VERSION: |
192 | 0 | case OFP14_VERSION: |
193 | 0 | case OFP15_VERSION: |
194 | 0 | osf->auxiliary_id = features->auxiliary_id; |
195 | | /* fall through */ |
196 | 0 | case OFP11_VERSION: |
197 | 0 | case OFP12_VERSION: |
198 | 0 | if (features->capabilities & OFPUTIL_C_GROUP_STATS) { |
199 | 0 | osf->capabilities |= htonl(OFPC11_GROUP_STATS); |
200 | 0 | } |
201 | 0 | break; |
202 | 0 | default: |
203 | 0 | OVS_NOT_REACHED(); |
204 | 0 | } |
205 | | |
206 | 0 | return b; |
207 | 0 | } |
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 | 0 | { |
216 | 0 | const struct ofp_header *oh = b->data; |
217 | |
|
218 | 0 | 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 | 0 | size_t start_ofs = b->size; |
223 | 0 | ofputil_put_phy_port(oh->version, pp, b); |
224 | 0 | if (b->size > UINT16_MAX) { |
225 | 0 | b->size = start_ofs; |
226 | 0 | } |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | static const char * |
231 | | ofputil_capabilities_to_name(uint32_t bit) |
232 | 16.5k | { |
233 | 16.5k | enum ofputil_capabilities capabilities = bit; |
234 | | |
235 | 16.5k | switch (capabilities) { |
236 | 3.01k | case OFPUTIL_C_FLOW_STATS: return "FLOW_STATS"; |
237 | 1.99k | case OFPUTIL_C_TABLE_STATS: return "TABLE_STATS"; |
238 | 1.88k | case OFPUTIL_C_PORT_STATS: return "PORT_STATS"; |
239 | 2.92k | case OFPUTIL_C_IP_REASM: return "IP_REASM"; |
240 | 1.90k | case OFPUTIL_C_QUEUE_STATS: return "QUEUE_STATS"; |
241 | 930 | case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP"; |
242 | 1.16k | case OFPUTIL_C_STP: return "STP"; |
243 | 773 | case OFPUTIL_C_GROUP_STATS: return "GROUP_STATS"; |
244 | 1.03k | case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED"; |
245 | 506 | case OFPUTIL_C_BUNDLES: return "BUNDLES"; |
246 | 437 | case OFPUTIL_C_FLOW_MONITORING: return "FLOW_MONITORING"; |
247 | 16.5k | } |
248 | | |
249 | 0 | return NULL; |
250 | 16.5k | } |
251 | | |
252 | | void |
253 | | ofputil_switch_features_format(struct ds *s, |
254 | | const struct ofputil_switch_features *features) |
255 | 5.11k | { |
256 | 5.11k | ds_put_format(s, " dpid:%016"PRIx64"\n", features->datapath_id); |
257 | | |
258 | 5.11k | ds_put_format(s, "n_tables:%"PRIu8", n_buffers:%"PRIu32, |
259 | 5.11k | features->n_tables, features->n_buffers); |
260 | 5.11k | if (features->auxiliary_id) { |
261 | 1.06k | ds_put_format(s, ", auxiliary_id:%"PRIu8, features->auxiliary_id); |
262 | 1.06k | } |
263 | 5.11k | ds_put_char(s, '\n'); |
264 | | |
265 | 5.11k | ds_put_cstr(s, "capabilities: "); |
266 | 5.11k | ofp_print_bit_names(s, features->capabilities, |
267 | 5.11k | ofputil_capabilities_to_name, ' '); |
268 | 5.11k | ds_put_char(s, '\n'); |
269 | | |
270 | 5.11k | if (features->ofpacts) { |
271 | 2.75k | ds_put_cstr(s, "actions: "); |
272 | 2.75k | ofpact_bitmap_format(features->ofpacts, s); |
273 | 2.75k | ds_put_char(s, '\n'); |
274 | 2.75k | } |
275 | 5.11k | } |
276 | | |
277 | | const char * |
278 | | ofputil_frag_handling_to_string(enum ofputil_frag_handling frag) |
279 | 8.62k | { |
280 | 8.62k | switch (frag) { |
281 | 4.65k | case OFPUTIL_FRAG_NORMAL: return "normal"; |
282 | 815 | case OFPUTIL_FRAG_DROP: return "drop"; |
283 | 1.79k | case OFPUTIL_FRAG_REASM: return "reassemble"; |
284 | 1.35k | case OFPUTIL_FRAG_NX_MATCH: return "nx-match"; |
285 | 8.62k | } |
286 | | |
287 | 8.62k | OVS_NOT_REACHED(); |
288 | 8.62k | } |
289 | | |
290 | | bool |
291 | | ofputil_frag_handling_from_string(const char *s, |
292 | | enum ofputil_frag_handling *frag) |
293 | 0 | { |
294 | 0 | if (!strcasecmp(s, "normal")) { |
295 | 0 | *frag = OFPUTIL_FRAG_NORMAL; |
296 | 0 | } else if (!strcasecmp(s, "drop")) { |
297 | 0 | *frag = OFPUTIL_FRAG_DROP; |
298 | 0 | } else if (!strcasecmp(s, "reassemble")) { |
299 | 0 | *frag = OFPUTIL_FRAG_REASM; |
300 | 0 | } else if (!strcasecmp(s, "nx-match")) { |
301 | 0 | *frag = OFPUTIL_FRAG_NX_MATCH; |
302 | 0 | } else { |
303 | 0 | return false; |
304 | 0 | } |
305 | 0 | return true; |
306 | 0 | } |
307 | | |
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 | 10.9k | { |
317 | 10.9k | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); |
318 | 10.9k | ofpraw_pull_assert(&b); |
319 | | |
320 | 10.9k | const struct ofp_switch_config *osc = ofpbuf_pull(&b, sizeof *osc); |
321 | 10.9k | config->frag = ntohs(osc->flags) & OFPC_FRAG_MASK; |
322 | 10.9k | config->miss_send_len = ntohs(osc->miss_send_len); |
323 | | |
324 | 10.9k | ovs_be16 valid_mask = htons(OFPC_FRAG_MASK); |
325 | 10.9k | if (oh->version < OFP13_VERSION) { |
326 | 6.74k | const ovs_be16 ttl_bit = htons(OFPC_INVALID_TTL_TO_CONTROLLER); |
327 | 6.74k | valid_mask |= ttl_bit; |
328 | 6.74k | config->invalid_ttl_to_controller = (osc->flags & ttl_bit) != 0; |
329 | 6.74k | } else { |
330 | 4.19k | config->invalid_ttl_to_controller = -1; |
331 | 4.19k | } |
332 | | |
333 | 10.9k | return !(osc->flags & ~valid_mask); |
334 | 10.9k | } |
335 | | |
336 | | void |
337 | | ofputil_decode_get_config_reply(const struct ofp_header *oh, |
338 | | struct ofputil_switch_config *config) |
339 | 8.18k | { |
340 | 8.18k | ofputil_decode_switch_config(oh, config); |
341 | 8.18k | } |
342 | | |
343 | | enum ofperr |
344 | | ofputil_decode_set_config(const struct ofp_header *oh, |
345 | | struct ofputil_switch_config *config) |
346 | 2.75k | { |
347 | 2.75k | return (ofputil_decode_switch_config(oh, config) |
348 | 2.75k | ? 0 |
349 | 2.75k | : OFPERR_OFPSCFC_BAD_FLAGS); |
350 | 2.75k | } |
351 | | |
352 | | static struct ofpbuf * |
353 | | ofputil_put_switch_config(const struct ofputil_switch_config *config, |
354 | | struct ofpbuf *b) |
355 | 0 | { |
356 | 0 | const struct ofp_header *oh = b->data; |
357 | 0 | struct ofp_switch_config *osc = ofpbuf_put_zeros(b, sizeof *osc); |
358 | 0 | osc->flags = htons(config->frag); |
359 | 0 | if (config->invalid_ttl_to_controller > 0 && oh->version < OFP13_VERSION) { |
360 | 0 | osc->flags |= htons(OFPC_INVALID_TTL_TO_CONTROLLER); |
361 | 0 | } |
362 | 0 | osc->miss_send_len = htons(config->miss_send_len); |
363 | 0 | return b; |
364 | 0 | } |
365 | | |
366 | | struct ofpbuf * |
367 | | ofputil_encode_get_config_reply(const struct ofp_header *request, |
368 | | const struct ofputil_switch_config *config) |
369 | 0 | { |
370 | 0 | struct ofpbuf *b = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY, |
371 | 0 | request, 0); |
372 | 0 | return ofputil_put_switch_config(config, b); |
373 | 0 | } |
374 | | |
375 | | struct ofpbuf * |
376 | | ofputil_encode_set_config(const struct ofputil_switch_config *config, |
377 | | enum ofp_version version) |
378 | 0 | { |
379 | 0 | struct ofpbuf *b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, version, 0); |
380 | 0 | return ofputil_put_switch_config(config, b); |
381 | 0 | } |
382 | | |
383 | | void |
384 | | ofputil_switch_config_format(struct ds *s, |
385 | | const struct ofputil_switch_config *config) |
386 | 8.62k | { |
387 | 8.62k | ds_put_format(s, " frags=%s", |
388 | 8.62k | ofputil_frag_handling_to_string(config->frag)); |
389 | | |
390 | 8.62k | if (config->invalid_ttl_to_controller > 0) { |
391 | 4.48k | ds_put_format(s, " invalid_ttl_to_controller"); |
392 | 4.48k | } |
393 | | |
394 | 8.62k | ds_put_format(s, " miss_send_len=%"PRIu16"\n", config->miss_send_len); |
395 | 8.62k | } |