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