Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2012, 2013, 2014, 2015, 2016 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 <errno.h>
19 : : #include "byte-order.h"
20 : : #include "openflow/openflow.h"
21 : : #include "openvswitch/dynamic-string.h"
22 : : #include "openvswitch/ofp-errors.h"
23 : : #include "openvswitch/ofp-msgs.h"
24 : : #include "openvswitch/ofp-util.h"
25 : : #include "openvswitch/ofpbuf.h"
26 : : #include "openvswitch/vlog.h"
27 : : #include "util.h"
28 : :
29 : 20190 : VLOG_DEFINE_THIS_MODULE(ofp_errors);
30 : :
31 : : struct triplet {
32 : : uint32_t vendor;
33 : : int type, code;
34 : : };
35 : :
36 : : #include "ofp-errors.inc"
37 : :
38 : : /* Returns an ofperr_domain that corresponds to the OpenFlow version number
39 : : * 'version' (one of the possible values of struct ofp_header's 'version'
40 : : * member). Returns NULL if the version isn't defined or isn't understood by
41 : : * OVS. */
42 : : static const struct ofperr_domain *
43 : 2463 : ofperr_domain_from_version(enum ofp_version version)
44 : : {
45 [ + + + + : 2463 : switch (version) {
+ + + + ]
46 : : case OFP10_VERSION:
47 : 56 : return &ofperr_of10;
48 : : case OFP11_VERSION:
49 : 37 : return &ofperr_of11;
50 : : case OFP12_VERSION:
51 : 36 : return &ofperr_of12;
52 : : case OFP13_VERSION:
53 : 1727 : return &ofperr_of13;
54 : : case OFP14_VERSION:
55 : 73 : return &ofperr_of14;
56 : : case OFP15_VERSION:
57 : 28 : return &ofperr_of15;
58 : : case OFP16_VERSION:
59 : 8 : return &ofperr_of16;
60 : : default:
61 : 498 : return NULL;
62 : : }
63 : : }
64 : :
65 : : /* Returns the name (e.g. "OpenFlow 1.0") of OpenFlow version 'version'. */
66 : : const char *
67 : 512 : ofperr_domain_get_name(enum ofp_version version)
68 : : {
69 : 512 : const struct ofperr_domain *domain = ofperr_domain_from_version(version);
70 [ + + ]: 512 : return domain ? domain->name : NULL;
71 : : }
72 : :
73 : : /* Returns true if 'error' is a valid OFPERR_* value, false otherwise. */
74 : : bool
75 : 3483 : ofperr_is_valid(enum ofperr error)
76 : : {
77 [ + + ][ + - ]: 3483 : return error >= OFPERR_OFS && error < OFPERR_OFS + OFPERR_N_ERRORS;
78 : : }
79 : :
80 : : /* Returns the OFPERR_* value that corresponds to 'type' and 'code' within
81 : : * 'version', or 0 if either no such OFPERR_* value exists or 'version' is
82 : : * unknown. */
83 : : static enum ofperr
84 : 1273 : ofperr_decode(enum ofp_version version,
85 : : uint32_t vendor, uint16_t type, uint16_t code)
86 : : {
87 : 1273 : const struct ofperr_domain *domain = ofperr_domain_from_version(version);
88 [ + - ]: 1273 : return domain ? domain->decode(vendor, type, code) : 0;
89 : : }
90 : :
91 : : /* Returns the name of 'error', e.g. "OFPBRC_BAD_TYPE" if 'error' is
92 : : * OFPBRC_BAD_TYPE, or "<invalid>" if 'error' is not a valid OFPERR_* value.
93 : : *
94 : : * Consider ofperr_to_string() instead, if the error code might be an errno
95 : : * value. */
96 : : const char *
97 : 1820 : ofperr_get_name(enum ofperr error)
98 : : {
99 : 3640 : return (ofperr_is_valid(error)
100 : 1820 : ? error_names[error - OFPERR_OFS]
101 [ + - ]: 1820 : : "<invalid>");
102 : : }
103 : :
104 : : /* Returns the OFPERR_* value that corresponds for 'name', 0 if none exists.
105 : : * For example, returns OFPERR_OFPHFC_INCOMPATIBLE if 'name' is
106 : : * "OFPHFC_INCOMPATIBLE".
107 : : *
108 : : * This is probably useful only for debugging and testing. */
109 : : enum ofperr
110 : 13 : ofperr_from_name(const char *name)
111 : : {
112 : : int i;
113 : :
114 [ + - ]: 737 : for (i = 0; i < OFPERR_N_ERRORS; i++) {
115 [ + + ]: 737 : if (!strcmp(name, error_names[i])) {
116 : 13 : return i + OFPERR_OFS;
117 : : }
118 : : }
119 : 0 : return 0;
120 : : }
121 : :
122 : : /* Returns an extended description name of 'error', e.g. "ofp_header.type not
123 : : * supported." if 'error' is OFPBRC_BAD_TYPE, or "<invalid>" if 'error' is not
124 : : * a valid OFPERR_* value. */
125 : : const char *
126 : 0 : ofperr_get_description(enum ofperr error)
127 : : {
128 : 0 : return (ofperr_is_valid(error)
129 : 0 : ? error_comments[error - OFPERR_OFS]
130 [ # # ]: 0 : : "<invalid>");
131 : : }
132 : :
133 : : static const struct triplet *
134 : 678 : ofperr_get_triplet__(enum ofperr error, const struct ofperr_domain *domain)
135 : : {
136 : 678 : size_t ofs = error - OFPERR_OFS;
137 : :
138 [ - + ]: 678 : ovs_assert(ofperr_is_valid(error));
139 : 678 : return &domain->errors[ofs];
140 : : }
141 : :
142 : : static struct ofpbuf *
143 : 636 : ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
144 : : ovs_be32 xid, const void *data, size_t data_len)
145 : : {
146 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
147 : : const struct ofperr_domain *domain;
148 : : const struct triplet *triplet;
149 : : struct ofp_error_msg *oem;
150 : : struct ofpbuf *buf;
151 : :
152 : : /* Get the error domain for 'ofp_version', or fall back to OF1.0. */
153 : 636 : domain = ofperr_domain_from_version(ofp_version);
154 [ - + ]: 636 : if (!domain) {
155 [ # # ]: 0 : VLOG_ERR_RL(&rl, "cannot encode error for unknown OpenFlow "
156 : : "version 0x%02x", ofp_version);
157 : 0 : domain = &ofperr_of10;
158 : : }
159 : :
160 : : /* Make sure 'error' is valid in 'domain', or use a fallback error. */
161 [ - + ]: 636 : if (!ofperr_is_valid(error)) {
162 : : /* 'error' seems likely to be a system errno value. */
163 [ # # ]: 0 : VLOG_ERR_RL(&rl, "invalid OpenFlow error code %d (%s)",
164 : : error, ovs_strerror(error));
165 : 0 : error = OFPERR_NXBRC_UNENCODABLE_ERROR;
166 [ + + ]: 636 : } else if (domain->errors[error - OFPERR_OFS].code < 0) {
167 [ + - ]: 1 : VLOG_ERR_RL(&rl, "cannot encode %s for %s",
168 : : ofperr_get_name(error), domain->name);
169 : 1 : error = OFPERR_NXBRC_UNENCODABLE_ERROR;
170 : : }
171 : :
172 : 636 : triplet = ofperr_get_triplet__(error, domain);
173 [ + + ]: 636 : if (!triplet->vendor) {
174 : 614 : buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
175 : : sizeof *oem + data_len);
176 : :
177 : 614 : oem = ofpbuf_put_uninit(buf, sizeof *oem);
178 : 614 : oem->type = htons(triplet->type);
179 : 614 : oem->code = htons(triplet->code);
180 [ + + ]: 22 : } else if (ofp_version <= OFP11_VERSION) {
181 : : struct nx_vendor_error *nve;
182 : :
183 : 11 : buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
184 : : sizeof *oem + sizeof *nve + data_len);
185 : :
186 : 11 : oem = ofpbuf_put_uninit(buf, sizeof *oem);
187 : 11 : oem->type = htons(NXET_VENDOR);
188 : 11 : oem->code = htons(NXVC_VENDOR_ERROR);
189 : :
190 : 11 : nve = ofpbuf_put_uninit(buf, sizeof *nve);
191 : 11 : nve->vendor = htonl(triplet->vendor);
192 : 11 : nve->type = htons(triplet->type);
193 : 11 : nve->code = htons(triplet->code);
194 : : } else {
195 : 11 : ovs_be32 vendor = htonl(triplet->vendor);
196 : :
197 : 11 : buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
198 : : sizeof *oem + sizeof(uint32_t) + data_len);
199 : :
200 : 11 : oem = ofpbuf_put_uninit(buf, sizeof *oem);
201 : 11 : oem->type = htons(OFPET12_EXPERIMENTER);
202 : 11 : oem->code = htons(triplet->type);
203 : 11 : ofpbuf_put(buf, &vendor, sizeof vendor);
204 : : }
205 : :
206 : 636 : ofpbuf_put(buf, data, data_len);
207 : 636 : ofpmsg_update_length(buf);
208 : :
209 : 636 : return buf;
210 : : }
211 : :
212 : : /* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the
213 : : * given 'error'.
214 : : *
215 : : * 'oh->version' determines the OpenFlow version of the error reply.
216 : : * 'oh->xid' determines the xid of the error reply.
217 : : * The error reply will contain an initial subsequence of 'oh', up to
218 : : * 'oh->length' or 64 bytes, whichever is shorter.
219 : : *
220 : : * This function isn't appropriate for encoding OFPET_HELLO_FAILED error
221 : : * messages. Use ofperr_encode_hello() instead. */
222 : : struct ofpbuf *
223 : 636 : ofperr_encode_reply(enum ofperr error, const struct ofp_header *oh)
224 : : {
225 : 636 : uint16_t len = ntohs(oh->length);
226 : :
227 [ + + ]: 636 : return ofperr_encode_msg__(error, oh->version, oh->xid, oh, MIN(len, 64));
228 : : }
229 : :
230 : : /* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the
231 : : * given 'error', in the error domain 'domain'. The error message will include
232 : : * the additional null-terminated text string 's'.
233 : : *
234 : : * If 'version' is an unknown version then OFP10_VERSION is used.
235 : : * OFPET_HELLO_FAILED error messages are supposed to be backward-compatible,
236 : : * so in theory this should work. */
237 : : struct ofpbuf *
238 : 0 : ofperr_encode_hello(enum ofperr error, enum ofp_version ofp_version,
239 : : const char *s)
240 : : {
241 : 0 : return ofperr_encode_msg__(error, ofp_version, htonl(0), s, strlen(s));
242 : : }
243 : :
244 : : int
245 : 14 : ofperr_get_vendor(enum ofperr error, enum ofp_version version)
246 : : {
247 : 14 : const struct ofperr_domain *domain = ofperr_domain_from_version(version);
248 [ + - ]: 14 : return domain ? ofperr_get_triplet__(error, domain)->vendor : -1;
249 : : }
250 : :
251 : : /* Returns the value that would go into an OFPT_ERROR message's 'type' for
252 : : * encoding 'error' in 'domain'. Returns -1 if 'error' is not encodable in
253 : : * 'version' or 'version' is unknown.
254 : : *
255 : : * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
256 : : int
257 : 14 : ofperr_get_type(enum ofperr error, enum ofp_version version)
258 : : {
259 : 14 : const struct ofperr_domain *domain = ofperr_domain_from_version(version);
260 [ + - ]: 14 : return domain ? ofperr_get_triplet__(error, domain)->type : -1;
261 : : }
262 : :
263 : : /* Returns the value that would go into an OFPT_ERROR message's 'code' for
264 : : * encoding 'error' in 'domain'. Returns -1 if 'error' is not encodable in
265 : : * 'version', 'version' is unknown or if 'error' represents a category
266 : : * rather than a specific error.
267 : : *
268 : : *
269 : : * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
270 : : int
271 : 14 : ofperr_get_code(enum ofperr error, enum ofp_version version)
272 : : {
273 : 14 : const struct ofperr_domain *domain = ofperr_domain_from_version(version);
274 [ + - ]: 14 : return domain ? ofperr_get_triplet__(error, domain)->code : -1;
275 : : }
276 : :
277 : : /* Tries to decode 'oh', which should be an OpenFlow OFPT_ERROR message.
278 : : * Returns an OFPERR_* constant on success, 0 on failure.
279 : : *
280 : : * If 'payload' is nonnull, on success '*payload' is initialized with a copy of
281 : : * the error's payload (copying is required because the payload is not properly
282 : : * aligned). The caller must free the payload (with ofpbuf_uninit()) when it
283 : : * is no longer needed. On failure, '*payload' is cleared. */
284 : : enum ofperr
285 : 1273 : ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
286 : : {
287 : : const struct ofp_error_msg *oem;
288 : : enum ofpraw raw;
289 : : uint16_t type, code;
290 : : uint32_t vendor;
291 : :
292 [ + + ]: 1273 : if (payload) {
293 : 1268 : memset(payload, 0, sizeof *payload);
294 : : }
295 : :
296 : : /* Pull off the error message. */
297 : 1273 : struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
298 : 1273 : enum ofperr error = ofpraw_pull(&raw, &b);
299 [ - + ]: 1273 : if (error) {
300 : 0 : return 0;
301 : : }
302 : 1273 : oem = ofpbuf_pull(&b, sizeof *oem);
303 : :
304 : : /* Get the error type and code. */
305 : 1273 : vendor = 0;
306 : 1273 : type = ntohs(oem->type);
307 : 1273 : code = ntohs(oem->code);
308 [ + + ][ + - ]: 1287 : if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) {
309 : 14 : const struct nx_vendor_error *nve = ofpbuf_try_pull(&b, sizeof *nve);
310 [ - + ]: 14 : if (!nve) {
311 : 0 : return 0;
312 : : }
313 : :
314 : 14 : vendor = ntohl(nve->vendor);
315 : 14 : type = ntohs(nve->type);
316 : 14 : code = ntohs(nve->code);
317 [ + + ]: 1259 : } else if (type == OFPET12_EXPERIMENTER) {
318 : 20 : const ovs_be32 *vendorp = ofpbuf_try_pull(&b, sizeof *vendorp);
319 [ - + ]: 20 : if (!vendorp) {
320 : 0 : return 0;
321 : : }
322 : :
323 : 20 : vendor = ntohl(*vendorp);
324 : 20 : type = code;
325 : 20 : code = 0;
326 : : }
327 : :
328 : : /* Translate the error type and code into an ofperr. */
329 : 1273 : error = ofperr_decode(oh->version, vendor, type, code);
330 [ + - ][ + + ]: 1273 : if (error && payload) {
331 : 1268 : ofpbuf_init(payload, b.size);
332 : 1268 : ofpbuf_push(payload, b.data, b.size);
333 : : }
334 : 1273 : return error;
335 : : }
336 : :
337 : : /* If 'error' is a valid OFPERR_* value, returns its name
338 : : * (e.g. "OFPBRC_BAD_TYPE" for OFPBRC_BAD_TYPE). Otherwise, assumes that
339 : : * 'error' is a positive errno value and returns what ovs_strerror() produces
340 : : * for 'error'. */
341 : : const char *
342 : 348 : ofperr_to_string(enum ofperr error)
343 : : {
344 : 696 : return (ofperr_is_valid(error)
345 : : ? ofperr_get_name(error)
346 [ + - ]: 348 : : ovs_strerror(error));
347 : : }
|