/**
   * Test to check whether the IPs given overlap or not at all
   *
   * @param flowIntersect - the current potential intersection
   * @param maskShift - the shift value (ie. either for a src_ip or a dst_ip).
   * @param masklenX - the netmask for the first ip.
   * @param masklenY - the netmask for the second ip.
   * @param x - the first ip
   * @param y - the second ip.
   * @return the smallest intersection of this ip space.
   */
  private int testIP(
      FlowIntersect flowIntersect, int maskShift, int masklenX, int masklenY, int x, int y) {
    int min = Math.min(masklenX, masklenY); // get the less specific address
    int max = Math.max(masklenX, masklenY); // get the more specific address
    int min_encoded = 32 - min; // because OpenFlow does it backwards... grr
    int max_encoded = 32 - max; // because OpenFlow does it backwards... grr
    if (max_encoded >= 32) // set all the bits if this is in fact fully
    max_encoded = 63; // wildcarded; if only for wireshark's sake

    int mask;
    if (min == 0) mask = 0; // nasty work around for stupid signed ints
    else mask = ~((1 << min_encoded) - 1); // min < 32, so no signed issues
    // int mask = (1 << min) - 1;

    if ((x & mask) != (y & mask)) // if these are not in the same CIDR block
    flowIntersect.setMatchType(MatchType.NONE);
    // else there is some overlap
    OFMatch interMatch = flowIntersect.getMatch();
    int wildCards = interMatch.getWildcards();
    // turn off all bits for this match and then turn on the used ones
    // use MAX not MIN, because we want the most specific intersection
    // split into two ops, so we can see intermediate step in debugger
    // assumes SRC mask == DST mask
    // turn off all bits for this match (making it an exact match)
    wildCards = wildCards & ~(((1 << OFMatch.OFPFW_NW_SRC_BITS) - 1) << maskShift);
    // turn on the bits for the intersection
    wildCards = wildCards | max_encoded << maskShift;
    interMatch.setWildcards(wildCards);
    if (masklenX < masklenY) {
      flowIntersect.maybeSubset = false;
      return y;
    } else if (masklenX > masklenY) {
      flowIntersect.maybeSuperset = false;
      return x;
    }

    // note that b/c of how CIDR addressing works, there is no overlap that
    // is not a SUB or SUPERSET
    return x; // x == y; doesn't matter
  }
  /**
   * Step through all of the partially computed results, compute the intersections and remove the
   * intersections by priority.
   *
   * <p>Could be O(n^2) in worst case, but we expect that intersections are rare (?)
   *
   * <p>Uses the fact that the order of the list is also the priority order
   *
   * <p>FIXME :: come back and make this faster
   *
   * @param mergeList List of all FlowEntry's from matches(), including overlaps.
   * @return A pruned list of just the non-completely-overlapping matches
   */
  List<FlowIntersect> priorityMerge(List<FlowIntersect> mergeList) {
    List<FlowIntersect> results = new ArrayList<FlowIntersect>();
    boolean eclipsed;
    MatchType matchType;
    results.add(mergeList.get(0));
    mergeList.remove(0);

    for (FlowIntersect merge : mergeList) {
      eclipsed = false;
      for (FlowIntersect result : results) {
        /*
         * is this new match eclipsed by previous entries?
         *
         * with each successive matches() call, the part that over laps
         * result is removed, so that if a merge rule is not fully
         * eclipsed by any one result, but is fully eclipsed by a sum of
         * results, we will catch that to
         */
        FlowIntersect tmpIntersect =
            merge.getFlowEntry().matches(result.getDpid(), result.getMatch());
        matchType = tmpIntersect.getMatchType();

        if ((matchType == MatchType.EQUAL) || (matchType == MatchType.SUPERSET)) {
          eclipsed = true;
          break;
        } else if (matchType == MatchType.SUBSET) {
          merge = tmpIntersect; // then update with the intersection
        }
        // note: if matchtype == NONE, then tmpIntersect.getMatch() is
        // undefined
      }
      if (!eclipsed) // add this match to the list iff it's
      results.add(merge); // not complete eclipsed by something before
      // it
    }
    return results;
  }
  /**
   * match is called when a switch sends a packet in to the controller. The goal is to find all flow
   * space rules which match the fields of the packet in.
   *
   * @param dpid - the datapath id of the switch which sent the packet in.
   * @param match - the packet in information
   * @return a list, sorted by priority, of flow space rules which match.
   */
  public List<FlowEntry> match(long dpid, FVMatch match) {
    BitSet set = new BitSet();
    LinkedList<FlowEntry> flowrules = new LinkedList<FlowEntry>();
    int wildcards = match.getWildcards();
    FVLog.log(LogLevel.DEBUG, null, "dpid: ", dpid, "match: ", match.toString());

    set.or(allRules);
    FVLog.log(LogLevel.DEBUG, null, "allRules: ", set.toString());

    try {

      testEmpty(set, dpids, dpid, FlowEntry.ALL_DPIDS, wildcards, 0);
      /*
       * Test every field and intersect the resulting bitset. If the bit
       * set is empty an exception is thrown to stop the search.
       */
      testEmpty(set, port, match.getInputPort(), ANY_IN_PORT, wildcards, FVMatch.OFPFW_IN_PORT);
      if (match.getDataLayerVirtualLan() != ANY_VLAN_ID) {
        testEmpty(
            set,
            vlan,
            (int) match.getDataLayerVirtualLan(),
            (int) ANY_VLAN_ID,
            wildcards,
            FVMatch.OFPFW_DL_VLAN);
        testEmpty(
            set,
            vlan,
            match.getDataLayerVirtualLanPriorityCodePoint() << 16,
            ANY_VLAN_PCP << 16,
            wildcards,
            FVMatch.OFPFW_DL_VLAN_PCP);
      } else {

        set.andNot(vlan_ignore);
      }

      testEmpty(
          set, dl_type, match.getDataLayerType(), ANY_ETHER, wildcards, FVMatch.OFPFW_DL_TYPE);

      testEmpty(
          set,
          nw,
          (short) match.getNetworkProtocol(),
          (short) ANY_NW_PROTO_TOS,
          wildcards,
          FVMatch.OFPFW_NW_PROTO);

      testEmpty(
          set,
          nw,
          (short) (match.getNetworkTypeOfService() << 8),
          (short) (ANY_NW_PROTO_TOS << 8),
          wildcards,
          FVMatch.OFPFW_NW_TOS);

      testEmpty(
          set, tp, (int) match.getTransportSource(), (int) ANY_TP, wildcards, FVMatch.OFPFW_TP_SRC);

      testEmpty(
          set,
          tp,
          match.getTransportDestination() << 16,
          ANY_TP << 16,
          wildcards,
          FVMatch.OFPFW_TP_DST);

      testEmpty(
          set,
          dl_src,
          FVMatch.toLong(match.getDataLayerSource()),
          ANY_MAC,
          wildcards,
          FVMatch.OFPFW_DL_SRC);

      testEmpty(
          set,
          dl_dst,
          FVMatch.toLong(match.getDataLayerDestination()),
          ANY_MAC,
          wildcards,
          FVMatch.OFPFW_DL_DST);

      for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i + 1)) {
        FlowEntry fe = rules.get(i);
        FVMatch ruleMatch = fe.getRuleMatch();
        FlowIntersect inter;
        inter = new FlowIntersect(fe.clone());

        FVMatch interMatch = inter.getMatch();
        interMatch.setNetworkDestination(
            testIP(
                inter,
                FVMatch.OFPFW_NW_DST_SHIFT,
                match.getNetworkDestinationMaskLen(),
                ruleMatch.getNetworkDestinationMaskLen(),
                match.getNetworkDestination(),
                ruleMatch.getNetworkDestination()));
        if (inter.getMatchType() == MatchType.NONE) {
          set.clear(i);
          continue;
        }

        // test ip_src
        interMatch.setNetworkSource(
            testIP(
                inter,
                FVMatch.OFPFW_NW_SRC_SHIFT,
                match.getNetworkSourceMaskLen(),
                ruleMatch.getNetworkSourceMaskLen(),
                match.getNetworkSource(),
                ruleMatch.getNetworkSource()));
        if (inter.getMatchType() == MatchType.NONE) {
          set.clear(i);
          continue;
        }
      }

    } catch (NoMatch e) {
      FVLog.log(LogLevel.INFO, null, "No match for: ", match);
      return flowrules;
    }

    /*
     * If we got here we have a match. Now return a prioritized list of
     * matches.
     *
     * Higher numbers have higher priorities.
     */

    TreeSet<FlowEntry> entries = new TreeSet<FlowEntry>();
    for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i + 1)) {
      entries.add(rules.get(i));
    }
    flowrules.addAll(entries);
    return flowrules;
  }
 /**
  * A big ugly method to set a field of an intersection.
  *
  * @param flowIntersect - the intersection to update
  * @param flowEntry - the flowentry from where to get the value from.
  * @param field - the field to update
  * @throws UnknownMatchField - Should never happen, but people can be funny.
  */
 private void setField(FlowIntersect flowIntersect, FVMatch match, int field)
     throws UnknownMatchField {
   flowIntersect.getMatch().setWildcards(flowIntersect.getMatch().getWildcards() & ~field);
   switch (field) {
     case FVMatch.OFPFW_DL_DST:
       flowIntersect.getMatch().setDataLayerDestination(match.getDataLayerDestination());
       break;
     case FVMatch.OFPFW_DL_SRC:
       flowIntersect.getMatch().setDataLayerSource(match.getDataLayerSource());
       break;
     case FVMatch.OFPFW_DL_TYPE:
       flowIntersect.getMatch().setDataLayerType(match.getDataLayerType());
       break;
     case FVMatch.OFPFW_IN_PORT:
       FVLog.log(LogLevel.DEBUG, null, "Setting input port");
       flowIntersect.getMatch().setInputPort(match.getInputPort());
       break;
     case FVMatch.OFPFW_DL_VLAN:
       flowIntersect.getMatch().setDataLayerVirtualLan(match.getDataLayerVirtualLan());
       break;
     case FVMatch.OFPFW_DL_VLAN_PCP:
       flowIntersect
           .getMatch()
           .setDataLayerVirtualLanPriorityCodePoint(
               match.getDataLayerVirtualLanPriorityCodePoint());
       break;
     case FVMatch.OFPFW_NW_SRC_ALL:
       flowIntersect.getMatch().setNetworkSource(match.getNetworkSource());
       break;
     case FVMatch.OFPFW_NW_DST_ALL:
       flowIntersect.getMatch().setNetworkDestination(match.getNetworkDestination());
       break;
     case FVMatch.OFPFW_NW_PROTO:
       flowIntersect.getMatch().setNetworkProtocol(match.getNetworkProtocol());
       break;
     case FVMatch.OFPFW_NW_TOS:
       flowIntersect.getMatch().setNetworkTypeOfService(match.getNetworkTypeOfService());
       break;
     case FVMatch.OFPFW_TP_SRC:
       flowIntersect.getMatch().setTransportSource(match.getTransportSource());
       break;
     case FVMatch.OFPFW_TP_DST:
       flowIntersect.getMatch().setTransportDestination(match.getTransportDestination());
       break;
     default:
       throw new UnknownMatchField("Unknown field type!");
   }
 }
  /**
   * This is called when a controller issues a FlowMod. The goal here is to rewrite flowmods so that
   * they guarantee traffic isolation between slices. A flowmod is rewritten following these rules:
   *
   * <p>1. Its field is wildcarded, then for every potentially matching rule we rewrite the field
   * with the value in the rule.
   *
   * <p>2. Its field is specified, then leave it untouched, but use it for matching purposes.
   *
   * <p>3. We have a potential partial match: 3.1. The field and the flowrule field do not overlap,
   * return no match. 3.2. The fields overlap, return the smallest possible intersection of the two.
   *
   * <p>Currently rule 3 only applies to IP addresses, but this could change in the future.
   *
   * @param dpid - The dpid this flowmod is directed to, could be wildcarded.
   * @param match - The set of field modifications issued by this flowmod.
   * @return a list of intersections (ie. flowmod rewrites) created by the flowmod issued from the
   *     controller.
   */
  public List<FlowIntersect> intersect(long dpid, FVMatch match) {
    FVLog.log(LogLevel.DEBUG, null, "dpid: ", dpid, " match: ", match.toString());
    BitSet set = new BitSet();
    normalize(match);
    int wildcards = match.getWildcards();
    TreeSet<FlowIntersect> ret = new TreeSet<FlowIntersect>();
    HashMap<Integer, FlowIntersect> intersections = new HashMap<Integer, FlowIntersect>();
    HashMap<Integer, Pair<Boolean, BitSet>> rewrites =
        new HashMap<Integer, Pair<Boolean, BitSet>>();

    set.or(allRules);

    try {

      testEmpty(set, dpids, dpid, FlowEntry.ALL_DPIDS, wildcards, 0);

      rewrites.put(
          FVMatch.OFPFW_IN_PORT,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set, port, match.getInputPort(), ANY_IN_PORT, wildcards, FVMatch.OFPFW_IN_PORT),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_SRC,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  dl_src,
                  FVMatch.toLong(match.getDataLayerSource()),
                  ANY_MAC,
                  wildcards,
                  FVMatch.OFPFW_DL_SRC),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_DST,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  dl_dst,
                  FVMatch.toLong(match.getDataLayerDestination()),
                  ANY_MAC,
                  wildcards,
                  FVMatch.OFPFW_DL_DST),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_VLAN,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  vlan,
                  (int) match.getDataLayerVirtualLan(),
                  (int) ANY_VLAN_ID,
                  wildcards,
                  FVMatch.OFPFW_DL_VLAN),
              set));

      rewrites.put(
          FVMatch.OFPFW_DL_VLAN_PCP,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  vlan,
                  match.getDataLayerVirtualLanPriorityCodePoint() << 16,
                  ANY_VLAN_PCP << 16,
                  wildcards,
                  FVMatch.OFPFW_DL_VLAN_PCP),
              set));
      rewrites.put(
          FVMatch.OFPFW_DL_TYPE,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  dl_type,
                  match.getDataLayerType(),
                  ANY_ETHER,
                  wildcards,
                  FVMatch.OFPFW_DL_TYPE),
              set));

      rewrites.put(
          FVMatch.OFPFW_NW_PROTO,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  nw,
                  (short) match.getNetworkProtocol(),
                  (short) ANY_NW_PROTO_TOS,
                  wildcards,
                  FVMatch.OFPFW_NW_PROTO),
              set));

      rewrites.put(
          FVMatch.OFPFW_NW_TOS,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  nw,
                  (short) (match.getNetworkTypeOfService() << 8),
                  (short) (ANY_NW_PROTO_TOS << 8),
                  wildcards,
                  FVMatch.OFPFW_NW_TOS),
              set));

      rewrites.put(
          FVMatch.OFPFW_TP_SRC,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  tp,
                  (int) match.getTransportSource(),
                  (int) ANY_TP,
                  wildcards,
                  FVMatch.OFPFW_TP_SRC),
              set));

      rewrites.put(
          FVMatch.OFPFW_TP_DST,
          new Pair<Boolean, BitSet>(
              testEmpty(
                  set,
                  tp,
                  match.getTransportDestination() << 16,
                  ANY_TP << 16,
                  wildcards,
                  FVMatch.OFPFW_TP_DST),
              set));

      int field = 0;
      boolean rewrite = false;
      BitSet inters = null;
      FlowIntersect flow = null;
      FlowEntry rule = null;

      for (Entry<Integer, Pair<Boolean, BitSet>> entry : rewrites.entrySet()) {
        field = entry.getKey();
        rewrite = entry.getValue().getFirst();
        inters = entry.getValue().getSecond();
        FVLog.log(LogLevel.DEBUG, null, "Rule ids which intersect: ", inters.toString());
        for (int i = inters.nextSetBit(0); i >= 0; i = inters.nextSetBit(i + 1)) {
          rule = rules.get(i).clone();
          flow = getIntersect(rule, intersections);

          if (!rewrite) {
            rule.setRuleMatch(match);
            setField(flow, rule.getRuleMatch(), field);
          }

          FVMatch ruleMatch = rules.get(i).getRuleMatch();
          FlowIntersect inter = flow;
          FVMatch interMatch = inter.getMatch();
          interMatch.setNetworkDestination(
              testIP(
                  inter,
                  FVMatch.OFPFW_NW_DST_SHIFT,
                  match.getNetworkDestinationMaskLen(),
                  ruleMatch.getNetworkDestinationMaskLen(),
                  match.getNetworkDestination(),
                  ruleMatch.getNetworkDestination()));
          if (inter.getMatchType() == MatchType.NONE) continue;

          interMatch.setNetworkSource(
              testIP(
                  inter,
                  FVMatch.OFPFW_NW_SRC_SHIFT,
                  match.getNetworkSourceMaskLen(),
                  ruleMatch.getNetworkSourceMaskLen(),
                  match.getNetworkSource(),
                  ruleMatch.getNetworkSource()));
          if (inter.getMatchType() == MatchType.NONE) continue;
          intersections.put(flow.getFlowEntry().getId(), flow);
        }
      }

      /*
       * Need to resolve intersection by priority.
       *
       * In the worst case this will be O(n) where n is the number of
       * rules. But in average we will only have a small subset of the
       * rules. Right??
       */
      ret.addAll(intersections.values());
      FVLog.log(LogLevel.DEBUG, null, "Intersections: ", intersections);

    } catch (NoMatch e) {
      FVLog.log(LogLevel.FATAL, null, "Failed to intersect flow mod " + match);
      return new ArrayList<FlowIntersect>(ret);
    } catch (UnknownMatchField umf) {
      FVLog.log(LogLevel.FATAL, null, umf.getMessage());
    }

    return new ArrayList<FlowIntersect>(ret);
  }