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