| ## List of mixed data types |
| # |
| # This is a list of data types which require special treatment |
| # because the underlying datatype has changed between versions. |
| # The main example is port which went from 16 to 32 bits. We |
| # define per-version accessors for these types and those are |
| # used in place of the normal ones. |
| # |
| # The wire protocol number is used to identify versions. For now, |
| # the value is the name of the type to use for that version |
| # |
| # This is the map between the external type (like of_port_no_t) |
| # which is used by customers of this code and the internal |
| # datatypes (like uint16_t) that appear on the wire for a |
| # particular version. |
| # |
| from collections import namedtuple |
| import logging |
| |
| import loxi_front_end.frontend_ir as fe |
| import loxi_ir.ir |
| |
| ofp_constants = dict( |
| OF_MAX_TABLE_NAME_LEN = 32, |
| OF_MAX_PORT_NAME_LEN = 16, |
| OF_ETH_ALEN = 6, |
| OF_DESC_STR_LEN = 256, |
| OF_SERIAL_NUM_LEN = 32 |
| ) |
| |
| |
| of_mixed_types = dict( |
| of_port_no_t = { |
| 1: "uint16_t", |
| 2: "uint32_t", |
| 3: "uint32_t", |
| 4: "uint32_t", |
| "short_name":"port_no" |
| }, |
| of_port_desc_t = { |
| 1: "of_port_desc_t", |
| 2: "of_port_desc_t", |
| 3: "of_port_desc_t", |
| 4: "of_port_desc_t", |
| "short_name":"port_desc" |
| }, |
| of_bsn_vport_t = { |
| 1: "of_bsn_vport_t", |
| 2: "of_bsn_vport_t", |
| 3: "of_bsn_vport_t", |
| 4: "of_bsn_vport_t", |
| "short_name":"bsn_vport" |
| }, |
| of_fm_cmd_t = { # Flow mod command went from u16 to u8 |
| 1: "uint16_t", |
| 2: "uint8_t", |
| 3: "uint8_t", |
| 4: "uint8_t", |
| "short_name":"fm_cmd" |
| }, |
| of_wc_bmap_t = { # Wildcard bitmap |
| 1: "uint32_t", |
| 2: "uint32_t", |
| 3: "uint64_t", |
| 4: "uint64_t", |
| "short_name":"wc_bmap" |
| }, |
| of_match_bmap_t = { # Match bitmap |
| 1: "uint32_t", |
| 2: "uint32_t", |
| 3: "uint64_t", |
| 4: "uint64_t", |
| "short_name":"match_bmap" |
| }, |
| of_match_t = { # Match object |
| 1: "of_match_v1_t", |
| 2: "of_match_v2_t", |
| 3: "of_match_v3_t", |
| 4: "of_match_v3_t", # Currently uses same match as 1.2 (v3). |
| "short_name":"match" |
| }, |
| ) |
| |
| ## basic lengths |
| of_base_lengths = dict( |
| char = (1, True), |
| uint8_t = (1, True), |
| uint16_t = (2, True), |
| uint32_t = (4, True), |
| uint64_t = (8, True), |
| of_mac_addr_t = (6, True), |
| of_ipv4_t = (4, True), |
| of_ipv6_t = (16, True), |
| of_port_name_t = (ofp_constants["OF_MAX_PORT_NAME_LEN"], True), |
| of_table_name_t = (ofp_constants["OF_MAX_TABLE_NAME_LEN"], True), |
| of_desc_str_t = (ofp_constants["OF_DESC_STR_LEN"], True), |
| of_serial_num_t = (ofp_constants["OF_SERIAL_NUM_LEN"], True), |
| of_match_v1_t = (40, True), |
| of_match_v2_t = (88, True), |
| of_match_v3_t = (8, False), |
| of_octets_t = (0, False), |
| of_bitmap_128_t = (16, True), |
| ) |
| |
| def type_dec_to_count_base(m_type): |
| """ |
| Resolve a type declaration like uint8_t[4] to a count (4) and base_type |
| (uint8_t) |
| |
| @param m_type The string type declaration to process |
| """ |
| count = 1 |
| chk_ar = m_type.split('[') |
| if len(chk_ar) > 1: |
| count_str = chk_ar[1].split(']')[0] |
| if count_str in ofp_constants: |
| count = ofp_constants[count_str] |
| else: |
| count = int(count_str) |
| base_type = chk_ar[0] |
| else: |
| base_type = m_type |
| return count, base_type |
| |
| |
| LengthInfo = namedtuple("LengthInfo", ("offset", "base_length", "is_fixed_length")) |
| |
| def calc_lengths(version, fe_class, existing_classes, existing_enums): |
| offset_fixed = True |
| offset = 0 |
| |
| member_infos = {} |
| for member in fe_class.members: |
| member_offset = offset if offset_fixed else None |
| |
| if isinstance(member, fe.OFPadMember): |
| member_base_length = member.length |
| member_fixed_length = True |
| else: |
| m_type = member.oftype |
| name = member.name |
| |
| member_base_length = 0 |
| if m_type.find("list(") == 0: |
| member_fixed_length = False |
| elif m_type.find("struct") == 0: |
| raise Exception("Error: recursive struct found: {}, {}" |
| .format(fe_class.name, name)) |
| elif m_type == "octets": |
| member_fixed_length = False |
| else: |
| member_base_length, member_fixed_length = member_length(version, fe_class, member, existing_classes, existing_enums) |
| |
| if not member_fixed_length: |
| offset_fixed = False |
| |
| member_infos[member] = LengthInfo(member_offset, member_base_length, |
| member_fixed_length) |
| offset += member_base_length |
| |
| base_length = offset |
| fixed_length = offset_fixed if not fe_class.virtual else False |
| return (base_length, fixed_length, member_infos) |
| |
| def member_length(version, fe_class, fe_member, existing_classes, existing_enums): |
| """ |
| return the length of an ir member. |
| |
| @return tuple (base_length, length_fixed) |
| """ |
| count, base_type = type_dec_to_count_base(fe_member.oftype) |
| |
| len_update = 0 |
| if base_type in of_mixed_types: |
| base_type = of_mixed_types[base_type][version.wire_version] |
| |
| base_class = base_type[:-2] |
| if base_class in existing_classes: |
| member_ir_class = existing_classes[base_class] |
| bytes = member_ir_class.base_length |
| length_fixed = member_ir_class.is_fixed_length |
| else: |
| if base_type in existing_enums: |
| enum = existing_enums[base_type] |
| base_type = enum.wire_type |
| |
| if base_type in of_base_lengths: |
| bytes, length_fixed = of_base_lengths[base_type] |
| else: |
| raise Exception("Unknown type for {}.{}: {}".format(fe_class.name, fe_member.name, base_type)) |
| |
| return (count * bytes), length_fixed |
| |
| def calculate_offsets_and_lengths(protocol): |
| for ir_class in protocol.classes: |
| offset_and_length_for_class(ir_class) |