/* * Copyright (c) 2013 Big Switch Networks, Inc. * * Licensed under the Eclipse Public License, Version 1.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.sdnplatform.devicegroup; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.openflow.util.HexString; import org.sdnplatform.core.IControllerService; import org.sdnplatform.devicemanager.IDevice; import org.sdnplatform.tagmanager.ITagManagerService; import org.sdnplatform.util.IPV4Subnet; import org.sdnplatform.util.IPV4SubnetTrie; /** * Concrete implementation of IDeviceGroupMatcher */ public class DeviceGroupMatcher<T extends IDeviceGroup> implements IDeviceGroupMatcher<T> { protected ITagManagerService tagManager; protected IControllerService controllerProvider; // Rule lookup data structures /** * Data structure for efficiently matching against MAC rules */ protected Map<String,Set<MembershipRule<T>>> macRuleMap; /** * Trie does prefix matching for IP subnets */ protected IPV4SubnetTrie<Set<MembershipRule<T>>> ipRuleTrie; /** * Data structure for matching against switch and port rules */ protected Map<String,Set<MembershipRule<T>>> switchPortMap; /** * Data structure for matching against vlan rules */ protected ArrayList<Set<MembershipRule<T>>> vlanList; protected boolean hasVlanRulesFlag; /** * Data structure for matching against tag rules * tagRuleMap: tag -> set<MembershipRule<T>> */ protected Map<String,Set<MembershipRule<T>>> tagRuleMap; /** * This is the list of rule matchers that will be used to * match against interface rules */ protected final ArrayList<AbstractRuleMatcher<T>> RULE_MATCHERS; /** * Add the rule to the match map */ protected void updateMatchMap(Map<String, Set<MembershipRule<T>>> matchMap, String key, MembershipRule<T> rule) { Set<MembershipRule<T>> rules = matchMap.get(key); if (rules == null) { matchMap.put(key, rules = new TreeSet<MembershipRule<T>>()); } rules.add(rule); } // Check if we have a particular kind of rules /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#hasMacRules() */ @Override public boolean hasMacRules() { return macRuleMap.size() != 0; } /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#hasIpSubnetRules() */ @Override public boolean hasIpSubnetRules() { return ipRuleTrie.size() != 0; } /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#hasSwitchPortRules() */ @Override public boolean hasSwitchPortRules() { return switchPortMap.size() != 0; } /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#hasVlanRules() */ @Override public boolean hasVlanRules() { return hasVlanRulesFlag; } /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#hasTagRules() */ @Override public boolean hasTagRules() { return tagRuleMap.size() != 0; } public DeviceGroupMatcher(ITagManagerService tagManager, IControllerService controllerProvider) { RULE_MATCHERS = new ArrayList<AbstractRuleMatcher<T>>(); RULE_MATCHERS.add(new MacMatcher<T>(this)); RULE_MATCHERS.add(new IpSubnetMatcher<T>(this)); RULE_MATCHERS.add(new SwitchPortMatcher<T>(this)); RULE_MATCHERS.add(new VlanMatcher<T>(this)); RULE_MATCHERS.add(new TagMatcher<T>(this)); macRuleMap = new HashMap<String,Set<MembershipRule<T>>>(); ipRuleTrie = new IPV4SubnetTrie<Set<MembershipRule<T>>>(); switchPortMap = new HashMap<String,Set<MembershipRule<T>>>(); tagRuleMap = new HashMap<String,Set<MembershipRule<T>>>(); vlanList = new ArrayList<Set<MembershipRule<T>>>(4096); for (int i=0; i<4096; i++) vlanList.add(null); this.controllerProvider = controllerProvider; this.tagManager = tagManager; } /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#clear() */ @Override public void clear() { // Flush rule-matching data structures macRuleMap.clear(); ipRuleTrie.clear(); switchPortMap.clear(); vlanList.clear(); for (int i=0; i<4096; i++) vlanList.add(null); hasVlanRulesFlag = false; tagRuleMap.clear(); } /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#addRuleIfActive(org.sdnplatform.devicegroup.MembershipRule) */ @Override public void addRuleIfActive(MembershipRule<T> rule) { if (!rule.isActive() || !rule.getParentDeviceGroup().isActive()) return; // Setup MAC matching data structure if (rule.getMac() != null) { updateMatchMap(macRuleMap, rule.getMac(), rule); } // Setup IP subnet matching data structure if (rule.getIpSubnet() != null) { IPV4Subnet s = new IPV4Subnet(rule.getIpSubnet()); Set<MembershipRule<T>> rules = ipRuleTrie.get(s); if (rules == null) { ipRuleTrie.put(s, rules = new TreeSet<MembershipRule<T>>()); } rules.add(rule); } // Setup switch/port matching data structure if (rule.getSwitchId() != null) { long switchId = HexString.toLong(rule.getSwitchId()); if (rule.getPorts() != null) { for (String p : rule.getPortList()) { String k = switchId + "-" + p; updateMatchMap(switchPortMap, k, rule); } } else { updateMatchMap(switchPortMap, Long.toString(switchId), rule); } } // Setup vlan matching data structure if (rule.getVlans() != null) { List<Integer> vlans = rule.getVlanList(); if (vlans != null) { for (Integer vlan : vlans) { Set<MembershipRule<T>> rules = vlanList.get(vlan); if (rules == null) { rules = new TreeSet<MembershipRule<T>>(); vlanList.set(vlan, rules); } rules.add(rule); } hasVlanRulesFlag = true; } } // Setup tag matching data structure if (rule.getTagList() != null) { for (String tag: rule.getTagList()) { updateMatchMap(tagRuleMap, tag, rule); } } } /* (non-Javadoc) * @see org.sdnplatform.devicegroup.IDeviceGroupMatcher#matchDevice(org.sdnplatform.devicemanager.IDevice) */ @Override public List<MembershipRule<T>> matchDevice(IDevice d) throws Exception { Map<AbstractRuleMatcher<T>, Set<MembershipRule<T>>> possibleMatches = null; // First generate a set of candidate matches by matching each of // the fields in the rules. for (AbstractRuleMatcher<T> matcher : RULE_MATCHERS) { Set<MembershipRule<T>> r = matcher.match(d); if (r != null && r.size() > 0) { if (possibleMatches == null) possibleMatches = new HashMap<AbstractRuleMatcher<T>, Set<MembershipRule<T>>>(); possibleMatches.put(matcher, r); } } if (possibleMatches == null) { // no matching interfaces; it will be assigned to the default NetVirt return null; } // Validate candidate matches by checking that all fields for a rule // are matched TreeSet<MembershipRule<T>> matches = new TreeSet<MembershipRule<T>>(); TreeSet<MembershipRule<T>> notmatches = new TreeSet<MembershipRule<T>>(); for (AbstractRuleMatcher<T> mname : possibleMatches.keySet()) { Set<MembershipRule<T>> candidates = possibleMatches.get(mname); for (MembershipRule<T> candidate : candidates) { if (matches.contains(candidate) || notmatches.contains(candidate)) continue; boolean allmatched = true; for (AbstractRuleMatcher<T> matcher : RULE_MATCHERS) { if (matcher.ruleHasField(candidate)) { Set<MembershipRule<T>> fieldcandidates = possibleMatches.get(matcher); if (fieldcandidates == null || !fieldcandidates.contains(candidate)) { allmatched = false; break; } } } if (allmatched) { matches.add(candidate); } else { notmatches.add(candidate); } } } if (matches.size() == 0) { // no matching interfaces; it will be assigned to the default // device group return null; } // Generate the final set of matched interfaces by traversing the // matching rules in priority order ArrayList<MembershipRule<T>> deviceRules = new ArrayList<MembershipRule<T>>(); T current = null; for (MembershipRule<T> match : matches) { if (match.isMultipleAllowed()) { if (!match.getParentDeviceGroup().equals(current)) { deviceRules.add(match); current = match.getParentDeviceGroup(); } } else if (deviceRules.size() == 0) { // Exactly one interface rule matches and we don't allow // multiple matches to a device group deviceRules.add(match); break; } } return deviceRules; } }