/** * Step through each FlowEntry in order and match on it. If we get EQUALS or SUBSET, then stop. * * <p>IF we get SUPERSET or INTERSECT, then keep going and merge the results. */ @Override public List<FlowIntersect> intersects(long dpid, OFMatch match) { List<FlowIntersect> results = new ArrayList<FlowIntersect>(); FlowIntersect intersect; MatchType matchType; boolean needMerge = false; for (Iterator<FlowEntry> it = rules.iterator(); it.hasNext(); ) { FlowEntry rule = it.next(); intersect = rule.matches(dpid, match); matchType = intersect.getMatchType(); if (matchType == MatchType.NONE) continue; results.add(intersect); if ((matchType == MatchType.EQUAL) || (matchType == MatchType.SUBSET)) break; if ((matchType == MatchType.INTERSECT) || (matchType == MatchType.SUPERSET)) needMerge = true; else // else, wtf? throw new RuntimeException("Unknown MatchType = " + intersect.getMatchType()); } if (needMerge && (results.size() > 1)) // BROKEN: needs to virtualize priorities // return priorityMerge(results); // expensive, avoid if possible return results; else return results; }
/** * 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); }