Esempio n. 1
0
/* Flow entry (a) matches flow entry (b) non-strictly if (a) matches whenever (b) matches.
 * Thus, flow (a) must not have more match fields than (b) and all match fields in (a) must
 * be equal or narrower in (b).
 * NOTE: Handling of bitmasked fields is not specified. In this implementation
 * a masked field of (a) matches the field of (b) if all masked bits of (b) are
 * also masked in (a), and for each unmasked bits of (b) , the bit is either
 * masked in (a), or is set to the same value in both matches.
 *
 */
bool
match_std_nonstrict(struct ofl_match *a, struct ofl_match *b, struct ofl_exp *exp)
{
    struct ofl_match_tlv *flow_mod_match;
    struct ofl_match_tlv *flow_entry_match;
    int field_len;
    uint8_t *flow_mod_val, *flow_mod_mask=0;
    uint8_t *flow_entry_val, *flow_entry_mask=0;
    bool has_mask;

    /* Flow a is fully wildcarded */
    if (!a->header.length)
        return true;

    /* Loop through the match fields in flow entry a */
    HMAP_FOR_EACH(flow_mod_match, struct ofl_match_tlv, hmap_node, &a->match_fields)
    {
        /* Check presence of match field in flow entry */
        flow_entry_match = oxm_match_lookup(flow_mod_match->header, b);
        if (!flow_entry_match) {
            return false;
        }

        /* At this point match length and has_mask are equal */
        has_mask = OXM_HASMASK(flow_mod_match->header);
        flow_mod_val = flow_mod_match->value;
        flow_entry_val = flow_entry_match->value;
        switch (OXM_VENDOR(flow_mod_match->header))
        {
            case (OFPXMC_OPENFLOW_BASIC):
                field_len =  OXM_LENGTH(flow_mod_match->header);
                if (has_mask)
                {
                    field_len /= 2;
                    flow_mod_mask = flow_mod_match->value + field_len;
                    flow_entry_mask = flow_entry_match->value + field_len;
                }
                break;
            case (OFPXMC_EXPERIMENTER):
                if (exp == NULL || exp->field == NULL || exp->field->match_std == NULL) {
                    VLOG_WARN(LOG_MODULE,"Received match is experimental, but no callback was given.");
                    ofl_error(OFPET_BAD_MATCH, OFPBMC_BAD_TYPE);
                }
                exp->field->match_std(flow_mod_match, flow_entry_match, &field_len, &flow_mod_val, &flow_entry_val, &flow_mod_mask, &flow_entry_mask);
                break;
            default:
                break;
        }

        switch (field_len) {
            case 1:
                if (has_mask) {
                    if (!nonstrict_mask8(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {
                    if (!match_8(flow_mod_val, flow_entry_val))
                        return false;
                }
                break;
            case 2:
                if (has_mask) {
                    if (!nonstrict_mask16(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {
                    if (!match_16(flow_mod_val, flow_entry_val))
                        return false;
                }
                break;
             case 3:
                if (has_mask) {
                    if (!nonstrict_mask24(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {
                    if (!match_24(flow_mod_val, flow_entry_val))
                        return false;
                }
                break;
            case 4:
                if (has_mask) {
                    if (!nonstrict_mask32(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {
                    if (!match_32(flow_mod_val, flow_entry_val))
                        return false;
                }
                break;
            case 6:
                if (has_mask) {
                    if (!nonstrict_mask48(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {
                    if (!match_48(flow_mod_val, flow_entry_val))
                        return false;
                }
                break;
            case 8:
                if (has_mask) {
                    if (!nonstrict_mask64(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {
                    if (!match_64(flow_mod_val, flow_entry_val))
                        return false;
                }
                break;
            case 16:
                if (has_mask) {
                    if (!nonstrict_mask128(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {
                    if (!match_128(flow_mod_val, flow_entry_val))
                        return false;
                }
                break;
            default:
                /* Should never happen */
                break;
        } /* switch (field_len) */

    } /* HMAP_FOR_EACH */

    /* If we get here, all match fields in flow a were equal or wider than the ones in b */
    /* It doesn't matter if there are further fields in b */
    return true;
}
Esempio n. 2
0
/* Returns true if two 24 bit values match */
static inline bool
match_24(uint8_t *a, uint8_t *b) {     
     return (match_16(a, b) &&
             match_8(a+2, b+2));
}
Esempio n. 3
0
/* Two matches strictly match if their wildcard fields are the same, and all the
 * non-wildcarded fields match on the same exact values.
 * NOTE: Handling of bitmasked fields is not specified. In this implementation
 * masked fields are checked for equality, and only unmasked bits are compared
 * in the field.
 */
bool
match_std_strict(struct ofl_match *a, struct ofl_match *b, struct ofl_exp *exp) {

    struct ofl_match_tlv *flow_mod_match;
    struct ofl_match_tlv *flow_entry_match;
    int field_len;
    uint8_t *flow_mod_val, *flow_mod_mask=0;
    uint8_t *flow_entry_val, *flow_entry_mask=0;
    uint8_t oxm_field;
    bool has_mask;
    /* Both matches all wildcarded */
    if(!a->header.length && !b->header.length )
        return true;

    /* If the matches differ in length, there is no reason to compare */
    if (a->header.length != b->header.length)
        return false;

    /* Loop through the flow_mod match fields */
    HMAP_FOR_EACH(flow_mod_match, struct ofl_match_tlv, hmap_node, &a->match_fields)
    {
        /* Check presence of match field in flow entry */
        flow_entry_match = oxm_match_lookup(flow_mod_match->header, b);
        if (!flow_entry_match) {
            return false;
        }

        /* At this point match length and has_mask are equal */
        oxm_field = OXM_FIELD(flow_mod_match->header);
        has_mask = OXM_HASMASK(flow_mod_match->header);
        flow_mod_val = flow_mod_match->value;
        flow_entry_val = flow_entry_match->value;
        switch (OXM_VENDOR(flow_mod_match->header))
        {
            case (OFPXMC_OPENFLOW_BASIC):         
                field_len =  OXM_LENGTH(flow_mod_match->header);
                if (has_mask)
                    {
                        field_len /= 2;
                        flow_mod_mask = flow_mod_val + field_len;
                        flow_entry_mask = flow_entry_val + field_len;
                    }
                break;
            case (OFPXMC_EXPERIMENTER):
                if (exp == NULL || exp->field == NULL || exp->field->match_std == NULL) {
                    VLOG_WARN(LOG_MODULE,"Received match is experimental, but no callback was given.");
                    ofl_error(OFPET_BAD_MATCH, OFPBMC_BAD_TYPE);
                }
                exp->field->match_std(flow_mod_match, flow_entry_match, &field_len, &flow_mod_val, &flow_entry_val, &flow_mod_mask, &flow_entry_mask);
                break;
            default:
                break;
        }
        
        switch (field_len) {
            case 1:
                if (has_mask) {
                    if (!strict_mask8(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask)){
                        return false;
                    }
                }
                else {
                    if (!match_8(flow_mod_val, flow_entry_val)){
                        return false;
                    }
                }
                break;
            case 2:
                if (has_mask) {
                    if (!strict_mask16(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask)){
                        return false;
                    }
                }
                else {
                    if (!match_16(flow_mod_val, flow_entry_val)){
                        return false;
                    }
                }
                break;
            case 3:
                if (has_mask) {
                    if (!strict_mask24(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                        return false;
                }
                else {                    
                    if (!match_24(flow_mod_val, flow_entry_val)){
                        return false;
                    }
                }
                break;
            case 4:
                if (has_mask) {
                    /* Quick and dirty fix for IP addresses matching 
                       TODO: Matching needs a huge refactoring  */
                    if (oxm_field == OFPXMT_OFB_IPV4_SRC ||
                        oxm_field == OFPXMT_OFB_IPV4_DST ||
                        oxm_field == OFPXMT_OFB_ARP_SPA ||
                        oxm_field == OFPXMT_OFB_ARP_TPA) {
                        if (!strict_mask_ip(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask)){
                            return false;
                        }
                    }
                    if (!strict_mask32(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask)){
                        return false;
                    }
                }
                else {

                    if (!match_32(flow_mod_val, flow_entry_val)){
                        return false;
                    }
                }
                break;
            case 6:
                if (has_mask) {
                    if (!strict_mask48(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask)){
                        return false;
                    }
                }
                else {
                    if (!match_48(flow_mod_val, flow_entry_val)){
                        return false;
                    }
                }
                break;
            case 8:
                if (has_mask) {
                    if (!strict_mask64(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask)){
                        return false;
                    }
                }
                else {
                    if (!match_64(flow_mod_val, flow_entry_val)){
                        return false;
                    }
                }
                break;
            case 16:
                if (has_mask) {
                    if (!strict_mask128(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask)){
                        return false;
                    }
                }
                else {
                    if (!match_128(flow_mod_val, flow_entry_val)){
                        return false;
                    }
                }
                break;
            default:
                /* Should never happen */
                break;
        } /* switch (field_len) */

    } /* HMAP_FOR_EACH */

    /* If we get here, all match fields in flow_mod were equal to the ones in flow entry */
    /* There can't be more fields in the flow entry as the lengths are the same */
    return true;
}
Esempio n. 4
0
/* Returns true if the fields in *packet matches the flow entry in *flow_match */
bool
packet_match(struct ofl_match *flow_match, struct ofl_match *packet, struct ofl_exp *exp){

    struct ofl_match_tlv *f;
    struct ofl_match_tlv *packet_f;
    bool has_mask;
    int field_len;
    int packet_header;
    uint8_t *flow_val, *flow_mask= NULL;
    uint8_t *packet_val;

    if (flow_match->header.length == 0)
        return true;
    
    /* Loop over the flow entry's match fields */
    HMAP_FOR_EACH(f, struct ofl_match_tlv, hmap_node, &flow_match->match_fields)
    {
        /* Check presence of match field in packet */

        has_mask = OXM_HASMASK(f->header);
        packet_header = f->header;
        switch (OXM_VENDOR(f->header))
        {
            case(OFPXMC_OPENFLOW_BASIC):
                field_len =  OXM_LENGTH(f->header);
                flow_val = f->value;
                if (has_mask) {
                    /* Clear the has_mask bit and divide the field_len by two in the packet field header */
                    field_len /= 2;
                    packet_header &= 0xfffffe00;
                    packet_header |= field_len;
                    flow_mask = f->value + field_len;
                }
                break;

            case(OFPXMC_EXPERIMENTER):
                if (exp == NULL || exp->field == NULL || exp->field->match == NULL) {
                    VLOG_WARN(LOG_MODULE,"Received match is experimental, but no callback was given.");
                    ofl_error(OFPET_BAD_MATCH, OFPBMC_BAD_TYPE);
                }
                exp->field->match(f, &packet_header, &field_len, &flow_val, &flow_mask);
                break;

            default:
                break;
        }
        
        /* Lookup the packet header */
        packet_f = oxm_match_lookup(packet_header, packet);
        if (!packet_f) {
        	if (f->header==OXM_OF_VLAN_VID &&
        			*((uint16_t *) f->value)==OFPVID_NONE) {
        		/* There is no VLAN tag, as required */
        		continue;
        	}
        	return false;
        }

        /* Compare the flow and packet field values, considering the mask, if any */
        switch (OXM_VENDOR(f->header))
        {
            case(OFPXMC_OPENFLOW_BASIC):
                packet_val = packet_f->value;
                break;
            case(OFPXMC_EXPERIMENTER):
                if (exp == NULL || exp->field == NULL || exp->field->compare == NULL) {
                    VLOG_WARN(LOG_MODULE,"Received match is experimental, but no callback was given.");
                    ofl_error(OFPET_BAD_MATCH, OFPBMC_BAD_TYPE);
                }
                exp->field->compare(f, packet_f, &packet_val);
                break;
            default:
                break;
        }

        switch (field_len) {
            case 1:
                if (has_mask) {
                    if (!match_mask8(flow_val, flow_mask, packet_val))
                        return false;
                }
                else {
                    if (!match_8(flow_val, packet_val))
                        return false;                    
                }
                break;
            case 2:
                switch (packet_header) {
                    case OXM_OF_VLAN_VID: {
                        /* Special handling for VLAN ID */
                        uint16_t flow_vlan_id = *((uint16_t*) flow_val);
                        if (flow_vlan_id == OFPVID_NONE) {
                            /* Packet has a VLAN tag when none should be there */
                            return false;
                        } else if (flow_vlan_id == OFPVID_PRESENT) {
                            /* Any VLAN ID is acceptable. No further checks */
                        } else {
                            /* Check the VLAN ID */
                            flow_vlan_id &= VLAN_VID_MASK;
                            if (has_mask){
                                if (!match_mask16((uint8_t*) &flow_vlan_id, flow_mask, packet_val)){
                                    return false;
                                }
                            }
                            else {
                                if (!match_16((uint8_t*) &flow_vlan_id, packet_val)){
                                    return false;
                                }
                            }
                        }
                        break;
                    }
                    case OXM_OF_IPV6_EXTHDR: {
                        /* Special handling for IPv6 Extension header */
                        uint16_t flow_eh = *((uint16_t *) flow_val);
                        uint16_t packet_eh = *((uint16_t *) packet_val);
                        if ((flow_eh & packet_eh) != flow_eh) {
                            /* The packet doesn't have all extension headers specified in the flow */
                            return false;
                        }
                        break;
                    }
                    default:
                        if (has_mask) {
                            if (!match_mask16(flow_val, flow_mask, packet_val))
                                return false;
                        }
                        else {
                            if (!match_16(flow_val, packet_val))
                                return false;
                        }
                        break;
                }
                break;
            case 3:
                if (has_mask) {
                    if (!match_mask24(flow_val, flow_mask, packet_val))
                        return false;
                }
                else {
                    if (!match_24(flow_val, packet_val))
                        return false;
                }
                break;
            case 4:
                if (has_mask) {
                    if (!match_mask32(flow_val, flow_mask, packet_val))
                        return false;
                }
                else {
                    if (!match_32(flow_val, packet_val))
                        return false;
                }
                break;
            case 6:
                if (has_mask) {
                    if (!match_mask48(flow_val, flow_mask, packet_val))
                        return false;
                }
                else {
                    if (!match_48(flow_val, packet_val))
                        return false;
                }
                break;
            case 8:
                if (has_mask) {
                    if (!match_mask64(flow_val, flow_mask, packet_val))
                        return false;
                }
                else {
                    if (!match_64(flow_val, packet_val))
                        return false;
                }
                break;
            case 16:
                if (has_mask) {
                    if (!match_mask128(flow_val, flow_mask, packet_val))
                        return false;
                }
                else {
                    if (!match_128(flow_val, packet_val))
                        return false;
                }
                break;
            default:
                /* Should never happen */
                break;
        }
    }
    /* If we get here, all match fields in the flow entry matched the packet */
    return true;
}
Esempio n. 5
0
/* Flow entry (a) matches flow entry (b) non-strictly if (a) matches whenever (b) matches.
 * Thus, flow (a) must not have more match fields than (b) and all match fields in (a) must
 * be equal or narrower in (b).
 * NOTE: Handling of bitmasked fields is not specified. In this implementation
 * a masked field of (a) matches the field of (b) if all masked bits of (b) are
 * also masked in (a), and for each unmasked bits of (b) , the bit is either
 * masked in (a), or is set to the same value in both matches.
 *
 */
bool
match_std_nonstrict(struct ofl_match *a, struct ofl_match *b)
{
    struct ofl_match_tlv *flow_mod_match;
    struct ofl_match_tlv *flow_entry_match;
    int field_len;
    uint8_t *flow_mod_val, *flow_mod_mask=0;
    uint8_t *flow_entry_val, *flow_entry_mask=0;
    bool has_mask;

    /* Flow a is fully wildcarded */
    if (!a->header.length)
        return true;

    /* Loop through the match fields in flow entry a */
    HMAP_FOR_EACH(flow_mod_match, struct ofl_match_tlv, hmap_node, &a->match_fields)
    {
        /* Check presence of match field in flow entry */
        flow_entry_match = oxm_match_lookup(flow_mod_match->header, b);
        if (!flow_entry_match) {
            return false;
        }

        /* At this point match length and has_mask are equal */
        has_mask = OXM_HASMASK(flow_mod_match->header);
        field_len =  OXM_LENGTH(flow_mod_match->header);
        flow_mod_val = flow_mod_match->value;
        flow_entry_val = flow_entry_match->value;
        if (has_mask)
        {
            field_len /= 2;
            flow_mod_mask = flow_mod_match->value + field_len;
            flow_entry_mask = flow_entry_match->value + field_len;
        }
        switch (field_len) {
        case 1:
            if (has_mask) {
                if (!nonstrict_mask8(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_8(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 2:
            if (has_mask) {
                if (!nonstrict_mask16(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_16(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 4:
            if (has_mask) {
                if (!nonstrict_mask32(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_32(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 6:
            if (has_mask) {
                if (!nonstrict_mask48(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_48(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 8:
            if (has_mask) {
                if (!nonstrict_mask64(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_64(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 16:
            if (has_mask) {
                if (!nonstrict_mask128(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_128(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        default:
            /* Should never happen */
            break;
        } /* switch (field_len) */

    } /* HMAP_FOR_EACH */

    /* If we get here, all match fields in flow a were equal or wider than the ones in b */
    /* It doesn't matter if there are further fields in b */
    return true;
}
Esempio n. 6
0
/* Two matches strictly match if their wildcard fields are the same, and all the
 * non-wildcarded fields match on the same exact values.
 * NOTE: Handling of bitmasked fields is not specified. In this implementation
 * masked fields are checked for equality, and only unmasked bits are compared
 * in the field.
 */
bool
match_std_strict(struct ofl_match *a, struct ofl_match *b) {

    struct ofl_match_tlv *flow_mod_match;
    struct ofl_match_tlv *flow_entry_match;
    int field_len;
    uint8_t *flow_mod_val, *flow_mod_mask=0;
    uint8_t *flow_entry_val, *flow_entry_mask=0;
    bool has_mask;

    /* Both matches all wildcarded */
    if(!a->header.length && !b->header.length )
        return true;

    /* If the matches differ in length, there is no reason to compare */
    if (a->header.length != b->header.length)
        return false;

    /* Loop through the flow_mod match fields */
    HMAP_FOR_EACH(flow_mod_match, struct ofl_match_tlv, hmap_node, &a->match_fields)
    {
        /* Check presence of match field in flow entry */
        flow_entry_match = oxm_match_lookup(flow_mod_match->header, b);
        if (!flow_entry_match) {
            return false;
        }

        /* At this point match length and has_mask are equal */
        has_mask = OXM_HASMASK(flow_mod_match->header);
        field_len =  OXM_LENGTH(flow_mod_match->header);
        flow_mod_val = flow_mod_match->value;
        flow_entry_val = flow_entry_match->value;
        if (has_mask)
        {
            field_len /= 2;
            flow_mod_mask = flow_mod_match->value + field_len;
            flow_entry_mask = flow_entry_match->value + field_len;
        }
        switch (field_len) {
        case 1:
            if (has_mask) {
                if (!strict_mask8(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_8(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 2:
            if (has_mask) {
                if (!strict_mask16(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_16(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 4:
            if (has_mask) {
                if (!strict_mask32(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_32(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 6:
            if (has_mask) {
                if (!strict_mask48(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_48(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 8:
            if (has_mask) {
                if (!strict_mask64(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_64(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        case 16:
            if (has_mask) {
                if (!strict_mask128(flow_mod_val, flow_entry_val, flow_mod_mask, flow_entry_mask))
                    return false;
            }
            else {
                if (!match_128(flow_mod_val, flow_entry_val))
                    return false;
            }
            break;
        default:
            /* Should never happen */
            break;
        } /* switch (field_len) */

    } /* HMAP_FOR_EACH */

    /* If we get here, all match fields in flow_mod were equal to the ones in flow entry */
    /* There can't be more fields in the flow entry as the lengths are the same */
    return true;
}
Esempio n. 7
0
/* Returns true if the fields in *packet matches the flow entry in *flow_match */
bool
packet_match(struct ofl_match *flow_match, struct ofl_match *packet) {

    struct ofl_match_tlv *f;
    struct ofl_match_tlv *packet_f;
    bool has_mask;
    int field_len;
    int packet_header;
    uint8_t *flow_val, *flow_mask=0;
    uint8_t *packet_val;

    if (flow_match->header.length == 0) {
        return true;
    }

    /* Loop over the flow entry's match fields */
    HMAP_FOR_EACH(f, struct ofl_match_tlv, hmap_node, &flow_match->match_fields)
    {
        /* Check presence of match field in packet */
        has_mask = OXM_HASMASK(f->header);
        field_len =  OXM_LENGTH(f->header);
        packet_header = f->header;
        flow_val = f->value;
        if (has_mask) {
            /* Clear the has_mask bit and divide the field_len by two in the packet field header */
            field_len /= 2;
            packet_header &= 0xfffffe00;
            packet_header |= field_len;
            flow_mask = f->value + field_len;
        }

        char *f_str = ofl_structs_oxm_tlv_to_string(f);
        free(f_str);

        /* Lookup the packet header */
        packet_f = oxm_match_lookup(packet_header, packet);
        if (!packet_f) {
            if (f->header==OXM_OF_VLAN_VID &&
                    *((uint16_t *) f->value)==OFPVID_NONE) {
                /* There is no VLAN tag, as required */
                continue;
            }
            return false;
        }

        /* Compare the flow and packet field values, considering the mask, if any */
        packet_val = packet_f->value;
        switch (field_len) {
        case 1:
            if (has_mask) {
                if (!match_mask8(flow_val, flow_mask, packet_val))
                    return false;
            }
            else {
                if (!match_8(flow_val, packet_val))
                    return false;
            }
            break;
        case 2:
            switch (packet_header) {
            case OXM_OF_VLAN_VID: {
                /* Special handling for VLAN ID */
                uint16_t flow_vlan_id = *((uint16_t *) flow_val);
                if (flow_vlan_id == OFPVID_NONE) {
                    /* Packet has a VLAN tag when none should be there */
                    return false;
                } else if (flow_vlan_id == OFPVID_PRESENT) {
                    /* Any VLAN ID is acceptable. No further checks */
                } else {
                    /* Check the VLAN ID */
                    if (!match_16(flow_val, packet_val))
                        return false;
                }
                break;
            }
            case OXM_OF_IPV6_EXTHDR: {
                /* Special handling for IPv6 Extension header */
                uint16_t flow_eh = *((uint16_t *) flow_val);
                uint16_t packet_eh = *((uint16_t *) packet_val);
                if ((flow_eh & packet_eh) != flow_eh) {
                    /* The packet doesn't have all extension headers specified in the flow */
                    return false;
                }
                break;
            }
            default:
                if (has_mask) {
                    if (!match_mask16(flow_val, flow_mask, packet_val))
                        return false;
                }
                else {
                    if (!match_16(flow_val, packet_val))
                        return false;
                }
                break;
            }
            break;
        case 4:
            if (has_mask) {
                if (!match_mask32(flow_val, flow_mask, packet_val))
                    return false;
            }
            else {
                if (!match_32(flow_val, packet_val))
                    return false;
            }
            break;
        case 6:
            if (has_mask) {
                if (!match_mask48(flow_val, flow_mask, packet_val))
                    return false;
            }
            else {
                if (!match_48(flow_val, packet_val))
                    return false;
            }
            break;
        case 8:
            if (has_mask) {
                if (!match_mask64(flow_val, flow_mask, packet_val))
                    return false;
            }
            else {
                if (!match_64(flow_val, packet_val))
                    return false;
            }
            break;
        case 16:
            if (has_mask) {
                if (!match_mask128(flow_val, flow_mask, packet_val))
                    return false;
            }
            else {
                if (!match_128(flow_val, packet_val))
                    return false;
            }
            break;
        default:
            /* Should never happen */
            break;
        }
    }
    /* If we get here, all match fields in the flow entry matched the packet */
    return true;
}