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