/* 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)); }
/* 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; }
/* 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; }
/* 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; }
/* Returns true if two 48 bit values match */ static inline bool match_48(uint8_t *a, uint8_t *b) { return (match_32(a, b) && match_16(a+4, b+4)); }
/* 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; }
/* 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; }
/* 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; }