/* * Copyright (c) 2017 Intel, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "openvswitch/ofp-ed-props.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/ofp-parse.h" #include "util.h" #include "lib/packets.h" enum ofperr decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop, struct ofpbuf *out OVS_UNUSED, size_t *remaining) { uint16_t prop_class = ntohs((*ofp_prop)->prop_class); uint8_t prop_type = (*ofp_prop)->type; size_t len = (*ofp_prop)->len; size_t pad_len = ROUND_UP(len, 8); if (len < sizeof **ofp_prop || pad_len > *remaining) { return OFPERR_OFPBAC_BAD_LEN; } switch (prop_class) { case OFPPPC_NSH: { switch (prop_type) { case OFPPPT_PROP_NSH_MDTYPE: { struct ofp_ed_prop_nsh_md_type *opnmt = ALIGNED_CAST(struct ofp_ed_prop_nsh_md_type *, *ofp_prop); if (len > sizeof(*opnmt) || len > *remaining) { return OFPERR_NXBAC_BAD_ED_PROP; } struct ofpact_ed_prop_nsh_md_type *pnmt = ofpbuf_put_zeros(out, sizeof *pnmt); pnmt->header.prop_class = prop_class; pnmt->header.type = prop_type; pnmt->header.len = len; pnmt->md_type = opnmt->md_type; break; } case OFPPPT_PROP_NSH_TLV: { struct ofp_ed_prop_nsh_tlv *opnt = ALIGNED_CAST(struct ofp_ed_prop_nsh_tlv *, *ofp_prop); size_t tlv_pad_len = ROUND_UP(opnt->tlv_len, 8); if (len != sizeof(*opnt) + tlv_pad_len || len > *remaining) { return OFPERR_NXBAC_BAD_ED_PROP; } struct ofpact_ed_prop_nsh_tlv *pnt = ofpbuf_put_uninit(out, sizeof(*pnt)); pnt->header.prop_class = prop_class; pnt->header.type = prop_type; pnt->header.len = len; pnt->tlv_class = opnt->tlv_class; pnt->tlv_type = opnt->tlv_type; pnt->tlv_len = opnt->tlv_len; ofpbuf_put(out, opnt->data, tlv_pad_len); break; } default: return OFPERR_NXBAC_UNKNOWN_ED_PROP; } break; } default: return OFPERR_NXBAC_UNKNOWN_ED_PROP; } *remaining -= pad_len; *ofp_prop = ALIGNED_CAST(const struct ofp_ed_prop_header *, ((char *)(*ofp_prop) + pad_len)); return 0; } enum ofperr encode_ed_prop(const struct ofpact_ed_prop **prop, struct ofpbuf *out OVS_UNUSED) { size_t prop_len; switch ((*prop)->prop_class) { case OFPPPC_NSH: { switch ((*prop)->type) { case OFPPPT_PROP_NSH_MDTYPE: { struct ofpact_ed_prop_nsh_md_type *pnmt = ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *, *prop); struct ofp_ed_prop_nsh_md_type *opnmt = ofpbuf_put_uninit(out, sizeof(*opnmt)); opnmt->header.prop_class = htons((*prop)->prop_class); opnmt->header.type = (*prop)->type; opnmt->header.len = offsetof(struct ofp_ed_prop_nsh_md_type, pad); opnmt->md_type = pnmt->md_type; memset(opnmt->pad, 0, sizeof opnmt->pad); prop_len = sizeof(*pnmt); break; } case OFPPPT_PROP_NSH_TLV: { struct ofpact_ed_prop_nsh_tlv *pnt = ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, *prop); struct ofp_ed_prop_nsh_tlv *opnt; size_t tlv_pad_len = ROUND_UP(pnt->tlv_len, 8); size_t len = sizeof(*opnt) + tlv_pad_len; opnt = ofpbuf_put_uninit(out, len); opnt->header.prop_class = htons((*prop)->prop_class); opnt->header.type = (*prop)->type; opnt->header.len = len; opnt->tlv_class = pnt->tlv_class; opnt->tlv_type = pnt->tlv_type; opnt->tlv_len = pnt->tlv_len; memcpy(opnt->data, pnt->data, tlv_pad_len); prop_len = sizeof(*pnt) + tlv_pad_len; break; } default: return OFPERR_OFPBAC_BAD_ARGUMENT; } break; } default: return OFPERR_OFPBAC_BAD_ARGUMENT; } *prop = ALIGNED_CAST(const struct ofpact_ed_prop *, ((char *)(*prop) + prop_len)); return 0; } bool parse_ed_prop_class(const char *str OVS_UNUSED, uint16_t *prop_class) { if (!strcmp(str,"basic")) { *prop_class = OFPPPC_BASIC; } else if (!strcmp(str,"ethernet")) { *prop_class = OFPPPC_BASIC; } else if (!strcmp(str,"mpls")) { *prop_class = OFPPPC_MPLS; } else if (!strcmp(str,"mpls_mc")) { *prop_class = OFPPPC_MPLS; } else if (!strcmp(str,"gre")) { *prop_class = OFPPPC_GRE; } else if (!strcmp(str,"gtp")) { *prop_class = OFPPPC_GTP; } else if (!strcmp(str,"nsh")) { *prop_class = OFPPPC_NSH; } else { return false; } return true; } bool parse_ed_prop_type(uint16_t prop_class, const char *str OVS_UNUSED, uint8_t *type OVS_UNUSED) { switch (prop_class) { case OFPPPC_NSH: if (!strcmp(str, "md_type")) { *type = OFPPPT_PROP_NSH_MDTYPE; return true; } else if (!strcmp(str, "tlv")) { *type = OFPPPT_PROP_NSH_TLV; return true; } else { return false; } default: return false; } } /* Parse the value of an encap/decap property based on property class * and type and append the parsed property in internal format to the * ofpbuf out. * Returns a malloced string in the event of a parse error. The caller * must free the string. */ char * parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED, const char *value, struct ofpbuf *out OVS_UNUSED) { char *error = NULL; if (value == NULL || *value == '\0') { return xstrdup("Value missing for encap property"); } switch (prop_class) { case OFPPPC_NSH: switch (prop_type) { case OFPPPT_PROP_NSH_MDTYPE: { /* Format: ":uint8_t". */ uint8_t md_type; error = str_to_u8(value, "md_type", &md_type); if (error != NULL) { return error; } if (md_type < 1 || md_type > 2) { return xstrdup("invalid md_type"); } struct ofpact_ed_prop_nsh_md_type *pnmt = ofpbuf_put_uninit(out, sizeof(*pnmt)); pnmt->header.prop_class = prop_class; pnmt->header.type = prop_type; pnmt->header.len = offsetof(struct ofp_ed_prop_nsh_md_type, pad); pnmt->md_type = md_type; break; } case OFPPPT_PROP_NSH_TLV: { /* Format: ":ovs_be16,:uint8_t,:hex_string" */ struct ofpact_ed_prop_nsh_tlv *pnt; uint16_t tlv_class; uint8_t tlv_type; char buf[256]; size_t tlv_value_len, padding; size_t start_ofs = out->size; if (!ovs_scan(value, "0x%"SCNx16",%"SCNu8",0x%251[0-9a-fA-F]", &tlv_class, &tlv_type, buf)) { return xasprintf("Invalid NSH TLV header: %s", value); } ofpbuf_put_uninit(out, sizeof(*pnt)); ofpbuf_put_hex(out, buf, &tlv_value_len); pnt = ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, ((char *)out->data + start_ofs)); padding = ROUND_UP(tlv_value_len, 8) - tlv_value_len; pnt->header.prop_class = prop_class; pnt->header.type = prop_type; pnt->header.len = sizeof(*pnt) + tlv_value_len + padding; pnt->tlv_class = htons(tlv_class); pnt->tlv_type = tlv_type; pnt->tlv_len = tlv_value_len; if (padding > 0) { ofpbuf_put_zeros(out, padding); } break; } default: /* Unsupported property types rejected before. */ OVS_NOT_REACHED(); } break; default: /* Unsupported property classes rejected before. */ OVS_NOT_REACHED(); } return NULL; } char * format_ed_prop_class(const struct ofpact_ed_prop *prop) { switch (prop->prop_class) { case OFPPPC_BASIC: return "basic"; case OFPPPC_MPLS: return "mpls"; case OFPPPC_GRE: return "gre"; case OFPPPC_GTP: return "gtp"; case OFPPPC_NSH: return "nsh"; default: OVS_NOT_REACHED(); } } char * format_ed_prop_type(const struct ofpact_ed_prop *prop) { switch (prop->prop_class) { case OFPPPC_NSH: switch (prop->type) { case OFPPPT_PROP_NSH_MDTYPE: return "md_type"; case OFPPPT_PROP_NSH_TLV: return "tlv"; default: OVS_NOT_REACHED(); } break; default: OVS_NOT_REACHED(); } } void format_ed_prop(struct ds *s OVS_UNUSED, const struct ofpact_ed_prop *prop) { switch (prop->prop_class) { case OFPPPC_NSH: switch (prop->type) { case OFPPPT_PROP_NSH_MDTYPE: { struct ofpact_ed_prop_nsh_md_type *pnmt = ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *, prop); ds_put_format(s, "%s=%d", format_ed_prop_type(prop), pnmt->md_type); return; } case OFPPPT_PROP_NSH_TLV: { struct ofpact_ed_prop_nsh_tlv *pnt = ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, prop); ds_put_format(s, "%s(0x%04x,%d,", format_ed_prop_type(prop), ntohs(pnt->tlv_class), pnt->tlv_type); ds_put_hex(s, pnt->data, pnt->tlv_len); ds_put_cstr(s,")"); return; } default: OVS_NOT_REACHED(); } default: OVS_NOT_REACHED(); } }