/src/openvswitch/lib/ofp-ed-props.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2017 Intel, 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 <sys/types.h> |
19 | | #include <netinet/in.h> |
20 | | #include <arpa/inet.h> |
21 | | #include "openvswitch/ofp-ed-props.h" |
22 | | #include "openvswitch/ofpbuf.h" |
23 | | #include "openvswitch/ofp-parse.h" |
24 | | #include "util.h" |
25 | | #include "lib/packets.h" |
26 | | |
27 | | |
28 | | enum ofperr |
29 | | decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop, |
30 | | struct ofpbuf *out OVS_UNUSED, |
31 | | size_t *remaining) |
32 | 5.82k | { |
33 | 5.82k | uint16_t prop_class = ntohs((*ofp_prop)->prop_class); |
34 | 5.82k | uint8_t prop_type = (*ofp_prop)->type; |
35 | 5.82k | size_t len = (*ofp_prop)->len; |
36 | 5.82k | size_t pad_len = ROUND_UP(len, 8); |
37 | | |
38 | 5.82k | if (len < sizeof **ofp_prop || pad_len > *remaining) { |
39 | 569 | return OFPERR_OFPBAC_BAD_LEN; |
40 | 569 | } |
41 | | |
42 | 5.25k | switch (prop_class) { |
43 | 4.80k | case OFPPPC_NSH: { |
44 | 4.80k | switch (prop_type) { |
45 | 2.84k | case OFPPPT_PROP_NSH_MDTYPE: { |
46 | 2.84k | struct ofp_ed_prop_nsh_md_type *opnmt = |
47 | 2.84k | ALIGNED_CAST(struct ofp_ed_prop_nsh_md_type *, *ofp_prop); |
48 | 2.84k | if (len > sizeof(*opnmt) || len > *remaining) { |
49 | 35 | return OFPERR_NXBAC_BAD_ED_PROP; |
50 | 35 | } |
51 | 2.80k | struct ofpact_ed_prop_nsh_md_type *pnmt = |
52 | 2.80k | ofpbuf_put_zeros(out, sizeof *pnmt); |
53 | 2.80k | pnmt->header.prop_class = prop_class; |
54 | 2.80k | pnmt->header.type = prop_type; |
55 | 2.80k | pnmt->header.len = len; |
56 | 2.80k | pnmt->md_type = opnmt->md_type; |
57 | 2.80k | break; |
58 | 2.84k | } |
59 | 1.41k | case OFPPPT_PROP_NSH_TLV: { |
60 | 1.41k | struct ofp_ed_prop_nsh_tlv *opnt = |
61 | 1.41k | ALIGNED_CAST(struct ofp_ed_prop_nsh_tlv *, *ofp_prop); |
62 | 1.41k | size_t tlv_pad_len = ROUND_UP(opnt->tlv_len, 8); |
63 | 1.41k | if (len != sizeof(*opnt) + tlv_pad_len || len > *remaining) { |
64 | 193 | return OFPERR_NXBAC_BAD_ED_PROP; |
65 | 193 | } |
66 | 1.21k | struct ofpact_ed_prop_nsh_tlv *pnt = |
67 | 1.21k | ofpbuf_put_uninit(out, sizeof(*pnt)); |
68 | 1.21k | pnt->header.prop_class = prop_class; |
69 | 1.21k | pnt->header.type = prop_type; |
70 | 1.21k | pnt->header.len = len; |
71 | 1.21k | pnt->tlv_class = opnt->tlv_class; |
72 | 1.21k | pnt->tlv_type = opnt->tlv_type; |
73 | 1.21k | pnt->tlv_len = opnt->tlv_len; |
74 | 1.21k | ofpbuf_put(out, opnt->data, tlv_pad_len); |
75 | 1.21k | break; |
76 | 1.41k | } |
77 | 556 | default: |
78 | 556 | return OFPERR_NXBAC_UNKNOWN_ED_PROP; |
79 | 4.80k | } |
80 | 4.02k | break; |
81 | 4.80k | } |
82 | 4.02k | default: |
83 | 444 | return OFPERR_NXBAC_UNKNOWN_ED_PROP; |
84 | 5.25k | } |
85 | | |
86 | 4.02k | *remaining -= pad_len; |
87 | 4.02k | *ofp_prop = ALIGNED_CAST(const struct ofp_ed_prop_header *, |
88 | 4.02k | ((char *)(*ofp_prop) + pad_len)); |
89 | 4.02k | return 0; |
90 | 5.25k | } |
91 | | |
92 | | enum ofperr |
93 | | encode_ed_prop(const struct ofpact_ed_prop **prop, |
94 | | struct ofpbuf *out OVS_UNUSED) |
95 | 477 | { |
96 | 477 | size_t prop_len; |
97 | | |
98 | 477 | switch ((*prop)->prop_class) { |
99 | 477 | case OFPPPC_NSH: { |
100 | 477 | switch ((*prop)->type) { |
101 | 477 | case OFPPPT_PROP_NSH_MDTYPE: { |
102 | 477 | struct ofpact_ed_prop_nsh_md_type *pnmt = |
103 | 477 | ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *, *prop); |
104 | 477 | struct ofp_ed_prop_nsh_md_type *opnmt = |
105 | 477 | ofpbuf_put_uninit(out, sizeof(*opnmt)); |
106 | 477 | opnmt->header.prop_class = htons((*prop)->prop_class); |
107 | 477 | opnmt->header.type = (*prop)->type; |
108 | 477 | opnmt->header.len = |
109 | 477 | offsetof(struct ofp_ed_prop_nsh_md_type, pad); |
110 | 477 | opnmt->md_type = pnmt->md_type; |
111 | 477 | memset(opnmt->pad, 0, sizeof opnmt->pad); |
112 | 477 | prop_len = sizeof(*pnmt); |
113 | 477 | break; |
114 | 0 | } |
115 | 0 | case OFPPPT_PROP_NSH_TLV: { |
116 | 0 | struct ofpact_ed_prop_nsh_tlv *pnt = |
117 | 0 | ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, *prop); |
118 | 0 | struct ofp_ed_prop_nsh_tlv *opnt; |
119 | 0 | size_t tlv_pad_len = ROUND_UP(pnt->tlv_len, 8); |
120 | 0 | size_t len = sizeof(*opnt) + tlv_pad_len; |
121 | 0 | opnt = ofpbuf_put_uninit(out, len); |
122 | 0 | opnt->header.prop_class = htons((*prop)->prop_class); |
123 | 0 | opnt->header.type = (*prop)->type; |
124 | 0 | opnt->header.len = len; |
125 | 0 | opnt->tlv_class = pnt->tlv_class; |
126 | 0 | opnt->tlv_type = pnt->tlv_type; |
127 | 0 | opnt->tlv_len = pnt->tlv_len; |
128 | 0 | memcpy(opnt->data, pnt->data, tlv_pad_len); |
129 | 0 | prop_len = sizeof(*pnt) + tlv_pad_len; |
130 | 0 | break; |
131 | 0 | } |
132 | 0 | default: |
133 | 0 | return OFPERR_OFPBAC_BAD_ARGUMENT; |
134 | 477 | } |
135 | 477 | break; |
136 | 477 | } |
137 | 477 | default: |
138 | 0 | return OFPERR_OFPBAC_BAD_ARGUMENT; |
139 | 477 | } |
140 | | |
141 | 477 | *prop = ALIGNED_CAST(const struct ofpact_ed_prop *, |
142 | 477 | ((char *)(*prop) + prop_len)); |
143 | 477 | return 0; |
144 | 477 | } |
145 | | |
146 | | bool |
147 | | parse_ed_prop_class(const char *str OVS_UNUSED, |
148 | | uint16_t *prop_class) |
149 | 2.70k | { |
150 | 2.70k | if (!strcmp(str,"basic")) { |
151 | 0 | *prop_class = OFPPPC_BASIC; |
152 | 2.70k | } else if (!strcmp(str,"ethernet")) { |
153 | 675 | *prop_class = OFPPPC_BASIC; |
154 | 2.02k | } else if (!strcmp(str,"mpls")) { |
155 | 1.22k | *prop_class = OFPPPC_MPLS; |
156 | 1.22k | } else if (!strcmp(str,"mpls_mc")) { |
157 | 292 | *prop_class = OFPPPC_MPLS; |
158 | 507 | } else if (!strcmp(str,"gre")) { |
159 | 0 | *prop_class = OFPPPC_GRE; |
160 | 507 | } else if (!strcmp(str,"gtp")) { |
161 | 0 | *prop_class = OFPPPC_GTP; |
162 | 507 | } else if (!strcmp(str,"nsh")) { |
163 | 507 | *prop_class = OFPPPC_NSH; |
164 | 507 | } else { |
165 | 0 | return false; |
166 | 0 | } |
167 | 2.70k | return true; |
168 | 2.70k | } |
169 | | |
170 | | bool |
171 | | parse_ed_prop_type(uint16_t prop_class, |
172 | | const char *str OVS_UNUSED, |
173 | | uint8_t *type OVS_UNUSED) |
174 | 765 | { |
175 | 765 | switch (prop_class) { |
176 | 762 | case OFPPPC_NSH: |
177 | 762 | if (!strcmp(str, "md_type")) { |
178 | 684 | *type = OFPPPT_PROP_NSH_MDTYPE; |
179 | 684 | return true; |
180 | 684 | } else if (!strcmp(str, "tlv")) { |
181 | 4 | *type = OFPPPT_PROP_NSH_TLV; |
182 | 4 | return true; |
183 | 74 | } else { |
184 | 74 | return false; |
185 | 74 | } |
186 | 3 | default: |
187 | 3 | return false; |
188 | 765 | } |
189 | 765 | } |
190 | | |
191 | | /* Parse the value of an encap/decap property based on property class |
192 | | * and type and append the parsed property in internal format to the |
193 | | * ofpbuf out. |
194 | | * Returns a malloced string in the event of a parse error. The caller |
195 | | * must free the string. |
196 | | */ |
197 | | |
198 | | char * |
199 | | parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED, |
200 | | const char *value, struct ofpbuf *out OVS_UNUSED) |
201 | 688 | { |
202 | 688 | char *error = NULL; |
203 | | |
204 | 688 | if (value == NULL || *value == '\0') { |
205 | 6 | return xstrdup("Value missing for encap property"); |
206 | 6 | } |
207 | | |
208 | 682 | switch (prop_class) { |
209 | 682 | case OFPPPC_NSH: |
210 | 682 | switch (prop_type) { |
211 | 681 | case OFPPPT_PROP_NSH_MDTYPE: { |
212 | | /* Format: "<md_type>:uint8_t". */ |
213 | 681 | uint8_t md_type; |
214 | 681 | error = str_to_u8(value, "md_type", &md_type); |
215 | 681 | if (error != NULL) { |
216 | 1 | return error; |
217 | 1 | } |
218 | 680 | if (md_type < 1 || md_type > 2) { |
219 | 3 | return xstrdup("invalid md_type"); |
220 | 3 | } |
221 | 677 | struct ofpact_ed_prop_nsh_md_type *pnmt = |
222 | 677 | ofpbuf_put_uninit(out, sizeof(*pnmt)); |
223 | 677 | pnmt->header.prop_class = prop_class; |
224 | 677 | pnmt->header.type = prop_type; |
225 | 677 | pnmt->header.len = |
226 | 677 | offsetof(struct ofp_ed_prop_nsh_md_type, pad); |
227 | 677 | pnmt->md_type = md_type; |
228 | 677 | break; |
229 | 680 | } |
230 | 1 | case OFPPPT_PROP_NSH_TLV: { |
231 | | /* Format: "<class>:ovs_be16,<type>:uint8_t,<val>:hex_string" */ |
232 | 1 | struct ofpact_ed_prop_nsh_tlv *pnt; |
233 | 1 | uint16_t tlv_class; |
234 | 1 | uint8_t tlv_type; |
235 | 1 | char buf[256]; |
236 | 1 | size_t tlv_value_len, padding; |
237 | 1 | size_t start_ofs = out->size; |
238 | | |
239 | 1 | if (!ovs_scan(value, "0x%"SCNx16",%"SCNu8",0x%251[0-9a-fA-F]", |
240 | 1 | &tlv_class, &tlv_type, buf)) { |
241 | 1 | return xasprintf("Invalid NSH TLV header: %s", value); |
242 | 1 | } |
243 | 0 | ofpbuf_put_uninit(out, sizeof(*pnt)); |
244 | 0 | ofpbuf_put_hex(out, buf, &tlv_value_len); |
245 | 0 | pnt = ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, |
246 | 0 | ((char *)out->data + start_ofs)); |
247 | 0 | padding = ROUND_UP(tlv_value_len, 8) - tlv_value_len; |
248 | 0 | pnt->header.prop_class = prop_class; |
249 | 0 | pnt->header.type = prop_type; |
250 | 0 | pnt->header.len = sizeof(*pnt) + tlv_value_len + padding; |
251 | 0 | pnt->tlv_class = htons(tlv_class); |
252 | 0 | pnt->tlv_type = tlv_type; |
253 | 0 | pnt->tlv_len = tlv_value_len; |
254 | 0 | if (padding > 0) { |
255 | 0 | ofpbuf_put_zeros(out, padding); |
256 | 0 | } |
257 | 0 | break; |
258 | 1 | } |
259 | 0 | default: |
260 | | /* Unsupported property types rejected before. */ |
261 | 0 | OVS_NOT_REACHED(); |
262 | 682 | } |
263 | 677 | break; |
264 | 677 | default: |
265 | | /* Unsupported property classes rejected before. */ |
266 | 0 | OVS_NOT_REACHED(); |
267 | 682 | } |
268 | | |
269 | 677 | return NULL; |
270 | 682 | } |
271 | | |
272 | | char * |
273 | | format_ed_prop_class(const struct ofpact_ed_prop *prop) |
274 | 0 | { |
275 | 0 | switch (prop->prop_class) { |
276 | 0 | case OFPPPC_BASIC: |
277 | 0 | return "basic"; |
278 | 0 | case OFPPPC_MPLS: |
279 | 0 | return "mpls"; |
280 | 0 | case OFPPPC_GRE: |
281 | 0 | return "gre"; |
282 | 0 | case OFPPPC_GTP: |
283 | 0 | return "gtp"; |
284 | 0 | case OFPPPC_NSH: |
285 | 0 | return "nsh"; |
286 | 0 | default: |
287 | 0 | OVS_NOT_REACHED(); |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | | char * |
292 | | format_ed_prop_type(const struct ofpact_ed_prop *prop) |
293 | 1.07k | { |
294 | 1.07k | switch (prop->prop_class) { |
295 | 1.07k | case OFPPPC_NSH: |
296 | 1.07k | switch (prop->type) { |
297 | 800 | case OFPPPT_PROP_NSH_MDTYPE: |
298 | 800 | return "md_type"; |
299 | 272 | case OFPPPT_PROP_NSH_TLV: |
300 | 272 | return "tlv"; |
301 | 0 | default: |
302 | 0 | OVS_NOT_REACHED(); |
303 | 1.07k | } |
304 | 0 | break; |
305 | 0 | default: |
306 | 0 | OVS_NOT_REACHED(); |
307 | 1.07k | } |
308 | 1.07k | } |
309 | | |
310 | | void |
311 | | format_ed_prop(struct ds *s OVS_UNUSED, |
312 | | const struct ofpact_ed_prop *prop) |
313 | 1.07k | { |
314 | 1.07k | switch (prop->prop_class) { |
315 | 1.07k | case OFPPPC_NSH: |
316 | 1.07k | switch (prop->type) { |
317 | 800 | case OFPPPT_PROP_NSH_MDTYPE: { |
318 | 800 | struct ofpact_ed_prop_nsh_md_type *pnmt = |
319 | 800 | ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *, prop); |
320 | 800 | ds_put_format(s, "%s=%d", format_ed_prop_type(prop), |
321 | 800 | pnmt->md_type); |
322 | 800 | return; |
323 | 0 | } |
324 | 272 | case OFPPPT_PROP_NSH_TLV: { |
325 | 272 | struct ofpact_ed_prop_nsh_tlv *pnt = |
326 | 272 | ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, prop); |
327 | 272 | ds_put_format(s, "%s(0x%04x,%d,", |
328 | 272 | format_ed_prop_type(prop), |
329 | 272 | ntohs(pnt->tlv_class), pnt->tlv_type); |
330 | 272 | ds_put_hex(s, pnt->data, pnt->tlv_len); |
331 | 272 | ds_put_cstr(s,")"); |
332 | 272 | return; |
333 | 0 | } |
334 | 0 | default: |
335 | 0 | OVS_NOT_REACHED(); |
336 | 1.07k | } |
337 | 0 | default: |
338 | 0 | OVS_NOT_REACHED(); |
339 | 1.07k | } |
340 | 1.07k | } |