| package net.onrc.onos.core.drivermanager; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import net.floodlightcontroller.core.IFloodlightProviderService.Role; |
| import net.floodlightcontroller.core.IOF13Switch; |
| import net.floodlightcontroller.core.IOFSwitch; |
| import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted; |
| import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted; |
| import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted; |
| import net.floodlightcontroller.core.internal.OFSwitchImplBase; |
| import net.floodlightcontroller.util.MACAddress; |
| import net.floodlightcontroller.util.OrderedCollection; |
| import net.onrc.onos.core.configmanager.INetworkConfigService; |
| import net.onrc.onos.core.configmanager.INetworkConfigService.NetworkConfigState; |
| import net.onrc.onos.core.configmanager.INetworkConfigService.SwitchConfigStatus; |
| import net.onrc.onos.core.configmanager.NetworkConfig.LinkConfig; |
| import net.onrc.onos.core.configmanager.NetworkConfig.SwitchConfig; |
| import net.onrc.onos.core.configmanager.NetworkConfigManager; |
| import net.onrc.onos.core.configmanager.PktLinkConfig; |
| import net.onrc.onos.core.configmanager.SegmentRouterConfig; |
| import net.onrc.onos.core.matchaction.MatchAction; |
| import net.onrc.onos.core.matchaction.MatchActionOperationEntry; |
| import net.onrc.onos.core.matchaction.MatchActionOperations; |
| import net.onrc.onos.core.matchaction.MatchActionOperations.Operator; |
| import net.onrc.onos.core.matchaction.action.Action; |
| import net.onrc.onos.core.matchaction.action.CopyTtlInAction; |
| import net.onrc.onos.core.matchaction.action.CopyTtlOutAction; |
| import net.onrc.onos.core.matchaction.action.DecMplsTtlAction; |
| import net.onrc.onos.core.matchaction.action.DecNwTtlAction; |
| import net.onrc.onos.core.matchaction.action.GroupAction; |
| import net.onrc.onos.core.matchaction.action.ModifyDstMacAction; |
| import net.onrc.onos.core.matchaction.action.ModifySrcMacAction; |
| import net.onrc.onos.core.matchaction.action.OutputAction; |
| import net.onrc.onos.core.matchaction.action.PopMplsAction; |
| import net.onrc.onos.core.matchaction.action.PushMplsAction; |
| import net.onrc.onos.core.matchaction.action.SetMplsBosAction; |
| import net.onrc.onos.core.matchaction.action.SetMplsIdAction; |
| import net.onrc.onos.core.matchaction.match.Ipv4Match; |
| import net.onrc.onos.core.matchaction.match.Match; |
| import net.onrc.onos.core.matchaction.match.MplsMatch; |
| import net.onrc.onos.core.matchaction.match.PacketMatch; |
| import net.onrc.onos.core.util.Dpid; |
| import net.onrc.onos.core.util.IPv4Net; |
| import net.onrc.onos.core.util.PortNumber; |
| |
| import org.projectfloodlight.openflow.protocol.OFAsyncGetReply; |
| import org.projectfloodlight.openflow.protocol.OFBarrierRequest; |
| import org.projectfloodlight.openflow.protocol.OFBucket; |
| import org.projectfloodlight.openflow.protocol.OFDescStatsReply; |
| import org.projectfloodlight.openflow.protocol.OFErrorMsg; |
| import org.projectfloodlight.openflow.protocol.OFFactory; |
| import org.projectfloodlight.openflow.protocol.OFFlowMod; |
| import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; |
| import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply; |
| import org.projectfloodlight.openflow.protocol.OFGroupType; |
| import org.projectfloodlight.openflow.protocol.OFMatchV3; |
| import org.projectfloodlight.openflow.protocol.OFMessage; |
| import org.projectfloodlight.openflow.protocol.OFOxmList; |
| import org.projectfloodlight.openflow.protocol.OFPortDesc; |
| import org.projectfloodlight.openflow.protocol.OFPortStatus; |
| import org.projectfloodlight.openflow.protocol.OFStatsReply; |
| import org.projectfloodlight.openflow.protocol.action.OFAction; |
| import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; |
| import org.projectfloodlight.openflow.protocol.match.Match.Builder; |
| import org.projectfloodlight.openflow.protocol.match.MatchField; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthDst; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4DstMasked; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsBos; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsLabel; |
| import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid; |
| import org.projectfloodlight.openflow.types.EthType; |
| import org.projectfloodlight.openflow.types.IPv4Address; |
| import org.projectfloodlight.openflow.types.IpProtocol; |
| import org.projectfloodlight.openflow.types.MacAddress; |
| import org.projectfloodlight.openflow.types.OFBooleanValue; |
| import org.projectfloodlight.openflow.types.OFBufferId; |
| import org.projectfloodlight.openflow.types.OFGroup; |
| import org.projectfloodlight.openflow.types.OFPort; |
| import org.projectfloodlight.openflow.types.OFVlanVidMatch; |
| import org.projectfloodlight.openflow.types.TableId; |
| import org.projectfloodlight.openflow.types.TransportPort; |
| import org.projectfloodlight.openflow.types.U32; |
| import org.projectfloodlight.openflow.util.HexString; |
| |
| /** |
| * OFDescriptionStatistics Vendor (Manufacturer Desc.): Stanford University, |
| * Ericsson Research and CPqD Research. Make (Hardware Desc.) : OpenFlow 1.3 |
| * Reference Userspace Switch Model (Datapath Desc.) : None Software : Serial : |
| * None |
| */ |
| public class OFSwitchImplCPqD13 extends OFSwitchImplBase implements IOF13Switch { |
| private AtomicBoolean driverHandshakeComplete; |
| private AtomicBoolean haltStateMachine; |
| private OFFactory factory; |
| private static final int OFPCML_NO_BUFFER = 0xffff; |
| // Configuration of asynch messages to controller. We need different |
| // asynch messages depending on role-equal or role-master. |
| // We don't want to get anything if we are slave. |
| private static final long SET_FLOW_REMOVED_MASK_MASTER = 0xf; |
| private static final long SET_PACKET_IN_MASK_MASTER = 0x7; |
| private static final long SET_PORT_STATUS_MASK_MASTER = 0x7; |
| private static final long SET_FLOW_REMOVED_MASK_EQUAL = 0x0; |
| private static final long SET_PACKET_IN_MASK_EQUAL = 0x0; |
| private static final long SET_PORT_STATUS_MASK_EQUAL = 0x7; |
| private static final long SET_ALL_SLAVE = 0x0; |
| |
| private static final long TEST_FLOW_REMOVED_MASK = 0xf; |
| private static final long TEST_PACKET_IN_MASK = 0x7; |
| private static final long TEST_PORT_STATUS_MASK = 0x7; |
| |
| private static final int TABLE_VLAN = 0; |
| private static final int TABLE_TMAC = 1; |
| private static final int TABLE_IPv4_UNICAST = 2; |
| private static final int TABLE_MPLS = 3; |
| private static final int TABLE_ACL = 5; |
| |
| private static final short MAX_PRIORITY = (short) 0xffff; |
| private static final short PRIORITY_MULTIPLIER = (short) 2046; |
| private static final short MIN_PRIORITY = 0x0; |
| |
| private long barrierXidToWaitFor = -1; |
| private DriverState driverState; |
| private final boolean usePipeline13; |
| private SegmentRouterConfig srConfig; |
| private ConcurrentMap<Dpid, Set<PortNumber>> neighbors; |
| private ConcurrentMap<PortNumber, Dpid> portToNeighbors; |
| private List<Integer> segmentIds; |
| private boolean isEdgeRouter; |
| private int sid; |
| private ConcurrentMap<NeighborSet, EcmpInfo> ecmpGroups; |
| private ConcurrentMap<String, List<Integer>> tunnelGroupIdTable; |
| private ConcurrentMap<PortNumber, ArrayList<NeighborSet>> portNeighborSetMap; |
| private AtomicInteger groupid; |
| |
| public OFSwitchImplCPqD13(OFDescStatsReply desc, boolean usePipeline13) { |
| super(); |
| haltStateMachine = new AtomicBoolean(false); |
| driverState = DriverState.INIT; |
| driverHandshakeComplete = new AtomicBoolean(false); |
| setSwitchDescription(desc); |
| neighbors = new ConcurrentHashMap<Dpid, Set<PortNumber>>(); |
| portToNeighbors = new ConcurrentHashMap<PortNumber, Dpid>(); |
| ecmpGroups = new ConcurrentHashMap<NeighborSet, EcmpInfo>(); |
| portNeighborSetMap = |
| new ConcurrentHashMap<PortNumber, ArrayList<NeighborSet>>(); |
| tunnelGroupIdTable = new ConcurrentHashMap<String, List<Integer>>(); |
| segmentIds = new ArrayList<Integer>(); |
| isEdgeRouter = false; |
| groupid = new AtomicInteger(0); |
| this.usePipeline13 = usePipeline13; |
| } |
| |
| // ***************************** |
| // OFSwitchImplBase |
| // ***************************** |
| |
| |
| /* (non-Javadoc) |
| * @see java.lang.Object#toString() |
| */ |
| @Override |
| public String toString() { |
| return "OFSwitchImplCPqD13 [" + ((channel != null) |
| ? channel.getRemoteAddress() : "?") |
| + " DPID[" + ((stringId != null) ? stringId : "?") + "]]"; |
| } |
| |
| @Override |
| public void startDriverHandshake() throws IOException { |
| log.debug("Starting driver handshake for sw {}", getStringId()); |
| if (startDriverHandshakeCalled) { |
| throw new SwitchDriverSubHandshakeAlreadyStarted(); |
| } |
| startDriverHandshakeCalled = true; |
| factory = getFactory(); |
| if (!usePipeline13) { |
| // Send packet-in to controller if a packet misses the first table |
| populateTableMissEntry(0, true, false, false, 0); |
| driverHandshakeComplete.set(true); |
| } else { |
| nextDriverState(); |
| } |
| } |
| |
| @Override |
| public boolean isDriverHandshakeComplete() { |
| if (!startDriverHandshakeCalled) |
| throw new SwitchDriverSubHandshakeNotStarted(); |
| return driverHandshakeComplete.get(); |
| } |
| |
| @Override |
| public void processDriverHandshakeMessage(OFMessage m) { |
| if (!startDriverHandshakeCalled) |
| throw new SwitchDriverSubHandshakeNotStarted(); |
| if (isDriverHandshakeComplete()) |
| throw new SwitchDriverSubHandshakeCompleted(m); |
| try { |
| processOFMessage(this, m); |
| } catch (IOException e) { |
| log.error("Error generated when processing OFMessage", e.getCause()); |
| } |
| } |
| |
| @Override |
| public String getSwitchDriverState() { |
| return driverState.toString(); |
| } |
| |
| public void removePortFromGroups(PortNumber port) { |
| /* FIX: removePortFromGroups is not working */ |
| |
| log.debug("removePortFromGroups: Remove port {} from Switch {}", |
| port, getStringId()); |
| ArrayList<NeighborSet> portNSSet = portNeighborSetMap.get(port); |
| if (portNSSet == null) |
| { |
| /* No Groups are created with this port yet */ |
| log.warn("removePortFromGroups: No groups exist with Switch {} port {}", |
| getStringId(), port); |
| return; |
| } |
| log.debug("removePortFromGroups: Neighborsets that the port {} is part" |
| + "of on Switch {} are {}", |
| port, getStringId(), portNSSet); |
| |
| for (NeighborSet ns : portNSSet) { |
| /* Delete the first matched bucket */ |
| EcmpInfo portEcmpInfo = ecmpGroups.get(ns); |
| Iterator<BucketInfo> it = portEcmpInfo.buckets.iterator(); |
| log.debug("removePortFromGroups: Group {} on Switch {} has {} buckets", |
| portEcmpInfo.groupId, getStringId(), |
| portEcmpInfo.buckets.size()); |
| while (it.hasNext()) { |
| BucketInfo bucket = it.next(); |
| if (bucket.outport.equals(port)) { |
| it.remove(); |
| } |
| } |
| log.debug("removePortFromGroups: Modifying Group on Switch {} " |
| + "and Neighborset {} with {}", |
| getStringId(), ns, portEcmpInfo); |
| modifyEcmpGroup(portEcmpInfo); |
| } |
| /* Don't delete the entry from portNeighborSetMap because |
| * when the port is up again this info is needed |
| */ |
| return; |
| } |
| |
| public void addPortToGroups(PortNumber port) { |
| log.debug("addPortToGroups: Add port {} to Switch {}", |
| port, getStringId()); |
| ArrayList<NeighborSet> portNSSet = portNeighborSetMap.get(port); |
| if (portNSSet == null) { |
| /* Unknown Port */ |
| log.warn("addPortToGroups: Switch {} port {} is unknown", |
| getStringId(), port); |
| return; |
| } |
| log.debug("addPortToGroups: Neighborsets that the port {} is part" |
| + "of on Switch {} are {}", |
| port, getStringId(), portNSSet); |
| |
| Dpid neighborDpid = portToNeighbors.get(port); |
| for (NeighborSet ns : portNSSet) { |
| EcmpInfo portEcmpInfo = ecmpGroups.get(ns); |
| /* Find if this port is already part of any bucket |
| * in this group |
| * NOTE: This is needed because in some cases |
| * (such as for configured network nodes), both driver and |
| * application detect the network elements and creates the |
| * buckets in the same group. This check is to avoid |
| * duplicate bucket creation in such scenarios |
| */ |
| List<BucketInfo> buckets = portEcmpInfo.buckets; |
| if (buckets == null) { |
| buckets = new ArrayList<BucketInfo>(); |
| portEcmpInfo.buckets = buckets; |
| } else { |
| Iterator<BucketInfo> it = buckets.iterator(); |
| boolean matchingBucketExist = false; |
| while (it.hasNext()) { |
| BucketInfo bucket = it.next(); |
| if (bucket.outport.equals(port)) { |
| matchingBucketExist = true; |
| break; |
| } |
| } |
| if (matchingBucketExist) { |
| log.warn("addPortToGroups: On Switch {} duplicate " |
| + "portAdd is called for port {} with buckets {}", |
| getStringId(), port, buckets); |
| continue; |
| } |
| } |
| BucketInfo b = new BucketInfo(neighborDpid, |
| MacAddress.of(srConfig.getRouterMac()), |
| getNeighborRouterMacAddress(neighborDpid), |
| port, |
| ns.getEdgeLabel()); |
| buckets.add(b); |
| log.debug("addPortToGroups: Modifying Group on Switch {} " |
| + "and Neighborset {} with {}", |
| getStringId(), ns, portEcmpInfo); |
| modifyEcmpGroup(portEcmpInfo); |
| } |
| return; |
| } |
| |
| @Override |
| public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) { |
| OrderedCollection<PortChangeEvent> events = super.processOFPortStatus(ps); |
| for (PortChangeEvent e : events) { |
| switch (e.type) { |
| case DELETE: |
| case DOWN: |
| log.debug("processOFPortStatus: sw {} Port {} DOWN", |
| getStringId(), e.port.getPortNo().getPortNumber()); |
| removePortFromGroups(PortNumber.uint32( |
| e.port.getPortNo().getPortNumber())); |
| break; |
| case UP: |
| log.debug("processOFPortStatus: sw {} Port {} UP", |
| getStringId(), e.port.getPortNo().getPortNumber()); |
| addPortToGroups(PortNumber.uint32( |
| e.port.getPortNo().getPortNumber())); |
| } |
| } |
| return events; |
| } |
| |
| // ***************************** |
| // Driver handshake state-machine |
| // ***************************** |
| |
| enum DriverState { |
| INIT, |
| SET_TABLE_MISS_ENTRIES, |
| SET_TABLE_VLAN_TMAC, |
| SET_GROUPS, |
| VERIFY_GROUPS, |
| SET_ADJACENCY_LABELS, |
| EXIT |
| } |
| |
| protected void nextDriverState() throws IOException { |
| DriverState currentState = driverState; |
| if (haltStateMachine.get()) { |
| return; |
| } |
| switch (currentState) { |
| case INIT: |
| driverState = DriverState.SET_TABLE_MISS_ENTRIES; |
| setTableMissEntries(); |
| sendHandshakeBarrier(); |
| break; |
| case SET_TABLE_MISS_ENTRIES: |
| driverState = DriverState.SET_TABLE_VLAN_TMAC; |
| getNetworkConfig(); |
| populateTableVlan(); |
| populateTableTMac(); |
| sendHandshakeBarrier(); |
| break; |
| case SET_TABLE_VLAN_TMAC: |
| driverState = DriverState.SET_GROUPS; |
| createGroups(); |
| sendHandshakeBarrier(); |
| break; |
| case SET_GROUPS: |
| driverState = DriverState.VERIFY_GROUPS; |
| verifyGroups(); |
| break; |
| case VERIFY_GROUPS: |
| driverState = DriverState.SET_ADJACENCY_LABELS; |
| assignAdjacencyLabels(); |
| break; |
| case SET_ADJACENCY_LABELS: |
| driverState = DriverState.EXIT; |
| driverHandshakeComplete.set(true); |
| break; |
| case EXIT: |
| default: |
| driverState = DriverState.EXIT; |
| log.error("Driver handshake has exited for sw: {}", getStringId()); |
| } |
| } |
| |
| void processOFMessage(IOFSwitch sw, OFMessage m) throws IOException { |
| switch (m.getType()) { |
| case BARRIER_REPLY: |
| processBarrierReply(m); |
| break; |
| |
| case ERROR: |
| processErrorMessage(m); |
| break; |
| |
| case GET_ASYNC_REPLY: |
| OFAsyncGetReply asrep = (OFAsyncGetReply) m; |
| decodeAsyncGetReply(asrep); |
| break; |
| |
| case PACKET_IN: |
| // not ready to handle packet-ins |
| break; |
| |
| case QUEUE_GET_CONFIG_REPLY: |
| // not doing queue config yet |
| break; |
| |
| case STATS_REPLY: |
| processStatsReply((OFStatsReply) m); |
| break; |
| |
| case ROLE_REPLY: // channelHandler should handle this |
| case PORT_STATUS: // channelHandler should handle this |
| case FEATURES_REPLY: // don't care |
| case FLOW_REMOVED: // don't care |
| default: |
| log.debug("Received message {} during switch-driver subhandshake " |
| + "from switch {} ... Ignoring message", m, sw.getStringId()); |
| } |
| } |
| |
| private void processStatsReply(OFStatsReply sr) { |
| switch (sr.getStatsType()) { |
| case AGGREGATE: |
| break; |
| case DESC: |
| break; |
| case EXPERIMENTER: |
| break; |
| case FLOW: |
| break; |
| case GROUP_DESC: |
| processGroupDesc((OFGroupDescStatsReply) sr); |
| break; |
| case GROUP_FEATURES: |
| processGroupFeatures((OFGroupFeaturesStatsReply) sr); |
| break; |
| case METER_CONFIG: |
| break; |
| case METER_FEATURES: |
| break; |
| case PORT_DESC: |
| break; |
| case TABLE_FEATURES: |
| break; |
| default: |
| break; |
| |
| } |
| } |
| |
| private void processErrorMessage(OFMessage m) { |
| log.error("Switch {} Error {} in DriverState", getStringId(), |
| (OFErrorMsg) m, driverState); |
| } |
| |
| private void processBarrierReply(OFMessage m) throws IOException { |
| if (m.getXid() == barrierXidToWaitFor) { |
| // Driver state-machine progresses to the next state. |
| // If Barrier messages is not received, then eventually |
| // the ChannelHandler state machine will timeout, and the switch |
| // will be disconnected. |
| nextDriverState(); |
| } else { |
| log.error("Received incorrect barrier-message xid {} (expected: {}) in " |
| + "switch-driver state {} for switch {}", m, barrierXidToWaitFor, |
| driverState, getStringId()); |
| } |
| } |
| |
| private void processGroupDesc(OFGroupDescStatsReply gdsr) { |
| log.info("Sw: {} Group Desc {}", getStringId(), gdsr); |
| try { |
| nextDriverState(); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| // ***************************** |
| // Utility methods |
| // ***************************** |
| |
| void setTableMissEntries() throws IOException { |
| // set all table-miss-entries |
| populateTableMissEntry(TABLE_VLAN, true, false, false, -1); |
| populateTableMissEntry(TABLE_TMAC, true, false, false, -1); |
| populateTableMissEntry(TABLE_IPv4_UNICAST, false, true, true, |
| TABLE_ACL); |
| populateTableMissEntry(TABLE_MPLS, false, true, true, |
| TABLE_ACL); |
| populateTableMissEntry(TABLE_ACL, false, false, false, -1); |
| } |
| |
| private void sendHandshakeBarrier() throws IOException { |
| long xid = getNextTransactionId(); |
| barrierXidToWaitFor = xid; |
| OFBarrierRequest br = getFactory() |
| .buildBarrierRequest() |
| .setXid(xid) |
| .build(); |
| write(br, null); |
| } |
| |
| /** |
| * Adds a table-miss-entry to a pipeline table. |
| * <p> |
| * The table-miss-entry can be added with 'write-actions' or |
| * 'apply-actions'. It can also add a 'goto-table' instruction. By default |
| * if none of the booleans in the call are set, then the table-miss entry is |
| * added with no instructions, which means that if a packet hits the |
| * table-miss-entry, pipeline execution will stop, and the action set |
| * associated with the packet will be executed. |
| * |
| * @param tableToAdd the table to where the table-miss-entry will be added |
| * @param toControllerNow as an APPLY_ACTION instruction |
| * @param toControllerWrite as a WRITE_ACTION instruction |
| * @param toTable as a GOTO_TABLE instruction |
| * @param tableToSend the table to send as per the GOTO_TABLE instruction it |
| * needs to be set if 'toTable' is true. Ignored of 'toTable' is |
| * false. |
| * @throws IOException |
| */ |
| @SuppressWarnings("unchecked") |
| private void populateTableMissEntry(int tableToAdd, boolean toControllerNow, |
| boolean toControllerWrite, |
| boolean toTable, int tableToSend) throws IOException { |
| OFOxmList oxmList = OFOxmList.EMPTY; |
| OFMatchV3 match = factory.buildMatchV3() |
| .setOxmList(oxmList) |
| .build(); |
| OFAction outc = factory.actions() |
| .buildOutput() |
| .setPort(OFPort.CONTROLLER) |
| .setMaxLen(OFPCML_NO_BUFFER) |
| .build(); |
| List<OFInstruction> instructions = new ArrayList<OFInstruction>(); |
| if (toControllerNow) { |
| // table-miss instruction to send to controller immediately |
| OFInstruction instr = factory.instructions() |
| .buildApplyActions() |
| .setActions(Collections.singletonList(outc)) |
| .build(); |
| instructions.add(instr); |
| } |
| |
| if (toControllerWrite) { |
| // table-miss instruction to write-action to send to controller |
| // this will be executed whenever the action-set gets executed |
| OFInstruction instr = factory.instructions() |
| .buildWriteActions() |
| .setActions(Collections.singletonList(outc)) |
| .build(); |
| instructions.add(instr); |
| } |
| |
| if (toTable) { |
| // table-miss instruction to goto-table x |
| OFInstruction instr = factory.instructions() |
| .gotoTable(TableId.of(tableToSend)); |
| instructions.add(instr); |
| } |
| |
| if (!toControllerNow && !toControllerWrite && !toTable) { |
| // table-miss has no instruction - at which point action-set will be |
| // executed - if there is an action to output/group in the action |
| // set |
| // the packet will be sent there, otherwise it will be dropped. |
| instructions = (List<OFInstruction>) Collections.EMPTY_LIST; |
| } |
| |
| OFMessage tableMissEntry = factory.buildFlowAdd() |
| .setTableId(TableId.of(tableToAdd)) |
| .setMatch(match) // match everything |
| .setInstructions(instructions) |
| .setPriority(MIN_PRIORITY) |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| write(tableMissEntry, null); |
| } |
| |
| private void getNetworkConfig() { |
| INetworkConfigService ncs = floodlightProvider.getNetworkConfigService(); |
| SwitchConfigStatus scs = ncs.checkSwitchConfig(new Dpid(getId())); |
| if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) { |
| srConfig = (SegmentRouterConfig) scs.getSwitchConfig(); |
| isEdgeRouter = srConfig.isEdgeRouter(); |
| sid = srConfig.getNodeSid(); |
| } else { |
| log.error("Switch not configured as Segment-Router"); |
| } |
| |
| List<LinkConfig> linkConfigList = ncs.getConfiguredAllowedLinks(); |
| setNeighbors(linkConfigList); |
| |
| if (isEdgeRouter) { |
| List<SwitchConfig> switchList = ncs.getConfiguredAllowedSwitches(); |
| getAllNodeSegmentIds(switchList); |
| } |
| } |
| |
| private void populateTableVlan() throws IOException { |
| List<OFMessage> msglist = new ArrayList<OFMessage>(); |
| for (OFPortDesc p : getPorts()) { |
| int pnum = p.getPortNo().getPortNumber(); |
| if (U32.of(pnum).compareTo(U32.of(OFPort.MAX.getPortNumber())) < 1) { |
| OFOxmInPort oxp = factory.oxms().inPort(p.getPortNo()); |
| OFOxmVlanVid oxv = factory.oxms() |
| .vlanVid(OFVlanVidMatch.UNTAGGED); |
| OFOxmList oxmList = OFOxmList.of(oxp, oxv); |
| OFMatchV3 match = factory.buildMatchV3() |
| .setOxmList(oxmList).build(); |
| |
| // TODO: match on vlan-tagged packets for vlans configured on |
| // subnet ports and strip-vlan |
| |
| // Do not need to add vlans |
| /*int vlanid = getVlanConfig(pnum); |
| OFOxmVlanVid vidToSet = factory.oxms() |
| .vlanVid(OFVlanVidMatch.ofVlan(vlanid)); |
| OFAction pushVlan = factory.actions().pushVlan(EthType.VLAN_FRAME); |
| OFAction setVlan = factory.actions().setField(vidToSet); |
| List<OFAction> actionlist = new ArrayList<OFAction>(); |
| actionlist.add(pushVlan); |
| actionlist.add(setVlan); |
| OFInstruction appAction = factory.instructions().buildApplyActions() |
| .setActions(actionlist).build();*/ |
| |
| OFInstruction gotoTbl = factory.instructions().buildGotoTable() |
| .setTableId(TableId.of(TABLE_TMAC)).build(); |
| List<OFInstruction> instructions = new ArrayList<OFInstruction>(); |
| // instructions.add(appAction); |
| instructions.add(gotoTbl); |
| OFMessage flowEntry = factory.buildFlowAdd() |
| .setTableId(TableId.of(TABLE_VLAN)) |
| .setMatch(match) |
| .setInstructions(instructions) |
| .setPriority(1000) // does not matter - all rules |
| // exclusive |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(flowEntry); |
| } |
| } |
| write(msglist); |
| log.debug("Adding {} port/vlan-rules in sw {}", msglist.size(), getStringId()); |
| } |
| |
| private void populateTableTMac() throws IOException { |
| // match for router-mac and ip-packets |
| OFOxmEthType oxe = factory.oxms().ethType(EthType.IPv4); |
| OFOxmEthDst dmac = factory.oxms().ethDst(getRouterMacAddr()); |
| OFOxmList oxmListIp = OFOxmList.of(dmac, oxe); |
| OFMatchV3 matchIp = factory.buildMatchV3() |
| .setOxmList(oxmListIp).build(); |
| OFInstruction gotoTblIp = factory.instructions().buildGotoTable() |
| .setTableId(TableId.of(TABLE_IPv4_UNICAST)).build(); |
| List<OFInstruction> instructionsIp = Collections.singletonList(gotoTblIp); |
| OFMessage ipEntry = factory.buildFlowAdd() |
| .setTableId(TableId.of(TABLE_TMAC)) |
| .setMatch(matchIp) |
| .setInstructions(instructionsIp) |
| .setPriority(1000) // strict priority required lower than |
| // multicastMac |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| |
| // match for router-mac and mpls packets |
| OFOxmEthType oxmpls = factory.oxms().ethType(EthType.MPLS_UNICAST); |
| OFOxmList oxmListMpls = OFOxmList.of(dmac, oxmpls); |
| OFMatchV3 matchMpls = factory.buildMatchV3() |
| .setOxmList(oxmListMpls).build(); |
| OFInstruction gotoTblMpls = factory.instructions().buildGotoTable() |
| .setTableId(TableId.of(TABLE_MPLS)).build(); |
| List<OFInstruction> instructionsMpls = Collections.singletonList(gotoTblMpls); |
| OFMessage mplsEntry = factory.buildFlowAdd() |
| .setTableId(TableId.of(TABLE_TMAC)) |
| .setMatch(matchMpls) |
| .setInstructions(instructionsMpls) |
| .setPriority(1001) // strict priority required lower than |
| // multicastMac |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| |
| log.debug("Adding termination-mac-rules in sw {}", getStringId()); |
| List<OFMessage> msglist = new ArrayList<OFMessage>(2); |
| msglist.add(ipEntry); |
| msglist.add(mplsEntry); |
| write(msglist); |
| } |
| |
| private MacAddress getRouterMacAddr() { |
| if (srConfig != null) { |
| return MacAddress.of(srConfig.getRouterMac()); |
| } else { |
| // return a dummy mac address - it will not be used |
| return MacAddress.of("00:00:00:00:00:00"); |
| } |
| } |
| |
| private boolean isEdgeRouter(Dpid ndpid) { |
| INetworkConfigService ncs = floodlightProvider.getNetworkConfigService(); |
| SwitchConfigStatus scs = ncs.checkSwitchConfig(ndpid); |
| if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) { |
| return ((SegmentRouterConfig) scs.getSwitchConfig()).isEdgeRouter(); |
| } else { |
| // TODO: return false if router not allowed |
| return false; |
| } |
| } |
| |
| private MacAddress getNeighborRouterMacAddress(Dpid ndpid) { |
| INetworkConfigService ncs = floodlightProvider.getNetworkConfigService(); |
| SwitchConfigStatus scs = ncs.checkSwitchConfig(ndpid); |
| if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) { |
| return MacAddress.of(((SegmentRouterConfig) scs.getSwitchConfig()) |
| .getRouterMac()); |
| } else { |
| // return a dummy mac address - it will not be used |
| return MacAddress.of("00:00:00:00:00:00"); |
| } |
| } |
| |
| private void setNeighbors(List<LinkConfig> linkConfigList) { |
| for (LinkConfig lg : linkConfigList) { |
| if (!lg.getType().equals(NetworkConfigManager.PKT_LINK)) { |
| continue; |
| } |
| PktLinkConfig plg = (PktLinkConfig) lg; |
| if (plg.getDpid1() == getId()) { |
| addNeighborAtPort(new Dpid(plg.getDpid2()), |
| PortNumber.uint32(plg.getPort1())); |
| } else if (plg.getDpid2() == getId()) { |
| addNeighborAtPort(new Dpid(plg.getDpid1()), |
| PortNumber.uint32(plg.getPort2())); |
| } |
| } |
| } |
| |
| private void addNeighborAtPort(Dpid neighborDpid, PortNumber portToNeighbor) { |
| /* Update NeighborToPort database */ |
| if (neighbors.get(neighborDpid) != null) { |
| neighbors.get(neighborDpid).add(portToNeighbor); |
| } else { |
| Set<PortNumber> ports = new HashSet<PortNumber>(); |
| ports.add(portToNeighbor); |
| neighbors.put(neighborDpid, ports); |
| } |
| |
| /* Update portToNeighbors database */ |
| if (portToNeighbors.get(portToNeighbor) == null) |
| portToNeighbors.put(portToNeighbor, neighborDpid); |
| } |
| |
| private void getAllNodeSegmentIds(List<SwitchConfig> switchList) { |
| for (SwitchConfig sc : switchList) { |
| /* TODO: Do we need to check if the SwitchConfig is of |
| * type SegmentRouter? |
| */ |
| if (sc.getDpid() == getId()) { |
| continue; |
| } |
| segmentIds.add(((SegmentRouterConfig) sc).getNodeSid()); |
| } |
| log.debug("getAllNodeSegmentIds: at sw {} are {}", |
| getStringId(), segmentIds); |
| } |
| |
| private boolean isSegmentIdSameAsNodeSegmentId(Dpid dpid, int sId) { |
| INetworkConfigService ncs = floodlightProvider.getNetworkConfigService(); |
| SwitchConfigStatus scs = ncs.checkSwitchConfig(dpid); |
| if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) { |
| return (((SegmentRouterConfig) scs.getSwitchConfig()). |
| getNodeSid() == sId); |
| } else { |
| // TODO: return false if router not allowed |
| return false; |
| } |
| } |
| |
| private Set<Set<Dpid>> getAllNeighborSets(Set<Dpid> neighbors) { |
| List<Dpid> list = new ArrayList<Dpid>(neighbors); |
| Set<Set<Dpid>> sets = new HashSet<Set<Dpid>>(); |
| /* get the number of elements in the neighbors */ |
| int elements = list.size(); |
| /* the number of members of a power set is 2^n |
| * including the empty set |
| */ |
| int powerElements = (1 << elements); |
| |
| /* run a binary counter for the number of power elements */ |
| for (long i = 1; i < powerElements; i++) { |
| Set<Dpid> dpidSubSet = new HashSet<Dpid>(); |
| for (int j = 0; j < elements; j++) { |
| if ((i >> j) % 2 == 1) { |
| dpidSubSet.add(list.get(j)); |
| } |
| } |
| /* NOTE: Avoid any pairings of edge routers only |
| * at a backbone router */ |
| boolean avoidEdgeRouterPairing = true; |
| if ((!isEdgeRouter) && (dpidSubSet.size() > 1)) { |
| for (Dpid dpid : dpidSubSet) { |
| if (!isEdgeRouter(dpid)) { |
| avoidEdgeRouterPairing = false; |
| break; |
| } |
| } |
| } |
| else |
| avoidEdgeRouterPairing = false; |
| |
| if (!avoidEdgeRouterPairing) |
| sets.add(dpidSubSet); |
| } |
| return sets; |
| } |
| |
| private void createGroupForANeighborSet(NeighborSet ns, int groupId) { |
| List<BucketInfo> buckets = new ArrayList<BucketInfo>(); |
| for (Dpid d : ns.getDpids()) { |
| for (PortNumber sp : neighbors.get(d)) { |
| BucketInfo b = new BucketInfo(d, |
| MacAddress.of(srConfig.getRouterMac()), |
| getNeighborRouterMacAddress(d), sp, |
| ns.getEdgeLabel()); |
| buckets.add(b); |
| |
| /* Update Port Neighborset map */ |
| ArrayList<NeighborSet> portNeighborSets = |
| portNeighborSetMap.get(sp); |
| if (portNeighborSets == null) { |
| portNeighborSets = new ArrayList<NeighborSet>(); |
| portNeighborSets.add(ns); |
| portNeighborSetMap.put(sp, portNeighborSets); |
| } |
| else |
| portNeighborSets.add(ns); |
| } |
| } |
| EcmpInfo ecmpInfo = new EcmpInfo(groupId, buckets); |
| setEcmpGroup(ecmpInfo); |
| ecmpGroups.put(ns, ecmpInfo); |
| log.debug( |
| "createGroupForANeighborSet: Creating ecmp group {} in sw {} " |
| + "for neighbor set {} with: {}", |
| groupId, getStringId(), ns, ecmpInfo); |
| return; |
| } |
| |
| private void createGroupForMplsLabel(int groupId, String nodeId, |
| int nextGroupId, boolean bos) { |
| List<BucketInfo> buckets = new ArrayList<BucketInfo>(); |
| BucketInfo bucket = new BucketInfo(nextGroupId, |
| Integer.parseInt(nodeId), bos); |
| buckets.add(bucket); |
| EcmpInfo ecmpInfo = new EcmpInfo(groupId, buckets); |
| setPolicyEcmpGroup(ecmpInfo); |
| // ecmpGroups.put(ns, ecmpInfo); |
| log.debug( |
| "createGroupForANeighborSet: Creating ecmp group {} in sw {} " |
| + "for pushing label {} and group to {}", |
| groupId, getStringId(), nodeId, nextGroupId); |
| return; |
| } |
| |
| /** |
| * createGroups creates ECMP groups for all ports on this router connected |
| * to other routers (in the OF network). The information for ports is |
| * gleaned from the configured links. If no links are configured no groups |
| * will be created, and it is up to the caller of the IOF13Switch API to |
| * create groups. |
| * <p> |
| * By default all ports connected to the same neighbor router will be part |
| * of the same ECMP group. In addition, groups will be created for all |
| * possible combinations of neighbor routers. |
| * <p> |
| * For example, consider this router (R0) connected to 3 neighbors (R1, R2, |
| * and R3). The following groups will be created in R0: |
| * <li>1) all ports to R1, |
| * <li>2) all ports to R2, |
| * <li>3) all ports to R3, |
| * <li>4) all ports to R1 and R2 |
| * <li>5) all ports to R1 and R3 |
| * <li>6) all ports to R2 and R3 |
| * <li>7) all ports to R1, R2, and R3 |
| */ |
| private void createGroups() { |
| |
| Set<Dpid> dpids = neighbors.keySet(); |
| if (dpids == null || dpids.isEmpty()) { |
| return; |
| } |
| /* Create all possible Neighbor sets from this router |
| * NOTE: Avoid any pairings of edge routers only |
| */ |
| Set<Set<Dpid>> powerSet = getAllNeighborSets(dpids); |
| log.debug("createGroups: The size of neighbor powerset for sw {} is {}", |
| getStringId(), powerSet.size()); |
| Set<NeighborSet> nsSet = new HashSet<NeighborSet>(); |
| for (Set<Dpid> combo : powerSet) { |
| if (combo.isEmpty()) |
| continue; |
| if (isEdgeRouter && !segmentIds.isEmpty()) { |
| for (Integer sId : segmentIds) { |
| NeighborSet ns = new NeighborSet(); |
| ns.addDpids(combo); |
| /* Check if the edge label being set is of the |
| * same node in the Neighbor set |
| */ |
| if ((combo.size() != 1) || |
| (!isSegmentIdSameAsNodeSegmentId( |
| combo.iterator().next(), sId))) { |
| ns.setEdgeLabel(sId); |
| } |
| nsSet.add(ns); |
| } |
| } else { |
| NeighborSet ns = new NeighborSet(); |
| ns.addDpids(combo); |
| nsSet.add(ns); |
| } |
| } |
| log.debug("createGroups: The neighborset with label for sw {} is {}", |
| getStringId(), nsSet); |
| |
| for (NeighborSet ns : nsSet) { |
| createGroupForANeighborSet(ns, groupid.incrementAndGet()); |
| } |
| } |
| |
| private class EcmpInfo { |
| int groupId; |
| List<BucketInfo> buckets; |
| |
| EcmpInfo(int gid, List<BucketInfo> bucketInfos) { |
| groupId = gid; |
| buckets = bucketInfos; |
| } |
| |
| @Override |
| public String toString() { |
| return "groupId: " + groupId + ", buckets: " + buckets; |
| } |
| } |
| |
| private class BucketInfo { |
| Dpid neighborDpid; |
| MacAddress srcMac; |
| MacAddress dstMac; |
| PortNumber outport; |
| int groupNo; |
| int mplsLabel; |
| boolean bos; |
| |
| BucketInfo(Dpid nDpid, MacAddress smac, MacAddress dmac, |
| PortNumber p, int label) { |
| neighborDpid = nDpid; |
| srcMac = smac; |
| dstMac = dmac; |
| outport = p; |
| mplsLabel = label; |
| groupNo = -1; |
| } |
| |
| BucketInfo(int no, int label, boolean b) { |
| neighborDpid = null; |
| srcMac = null; |
| dstMac = null; |
| outport = null; |
| groupNo = no; |
| mplsLabel = label; |
| bos = b; |
| } |
| |
| |
| @Override |
| public String toString() { |
| return " {neighborDpid: " + neighborDpid + ", dstMac: " + dstMac + |
| ", srcMac: " + srcMac + ", outport: " + outport + |
| ", groupNo: " + groupNo + |
| ", mplsLabel: " + mplsLabel + "}"; |
| } |
| } |
| |
| private void setEcmpGroup(EcmpInfo ecmpInfo) { |
| List<OFMessage> msglist = new ArrayList<OFMessage>(); |
| OFGroup group = OFGroup.of(ecmpInfo.groupId); |
| |
| List<OFBucket> buckets = new ArrayList<OFBucket>(); |
| for (BucketInfo b : ecmpInfo.buckets) { |
| OFOxmEthDst dmac = factory.oxms() |
| .ethDst(b.dstMac); |
| OFAction setDA = factory.actions().buildSetField() |
| .setField(dmac).build(); |
| OFOxmEthSrc smac = factory.oxms() |
| .ethSrc(b.srcMac); |
| OFAction setSA = factory.actions().buildSetField() |
| .setField(smac).build(); |
| OFAction outp = factory.actions().buildOutput() |
| .setPort(OFPort.of(b.outport.shortValue())) |
| .build(); |
| List<OFAction> actions = new ArrayList<OFAction>(); |
| actions.add(setSA); |
| actions.add(setDA); |
| actions.add(outp); |
| if (b.mplsLabel != -1) { |
| OFAction pushLabel = factory.actions().buildPushMpls() |
| .setEthertype(EthType.MPLS_UNICAST).build(); |
| OFOxmMplsBos bosX = factory.oxms() |
| .mplsBos(OFBooleanValue.TRUE); |
| OFAction setBX = factory.actions().buildSetField() |
| .setField(bosX).build(); |
| OFOxmMplsLabel lid = factory.oxms() |
| .mplsLabel(U32.of(b.mplsLabel)); |
| OFAction setLabel = factory.actions().buildSetField() |
| .setField(lid).build(); |
| OFAction copyTtl = factory.actions().copyTtlOut(); |
| OFAction decrTtl = factory.actions().decMplsTtl(); |
| actions.add(pushLabel); |
| actions.add(setLabel); |
| actions.add(setBX); |
| actions.add(copyTtl); |
| actions.add(decrTtl); |
| } |
| OFBucket ofb = factory.buildBucket() |
| .setWeight(1) |
| .setActions(actions) |
| .build(); |
| buckets.add(ofb); |
| } |
| |
| OFMessage gm = factory.buildGroupAdd() |
| .setGroup(group) |
| .setBuckets(buckets) |
| .setGroupType(OFGroupType.SELECT) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(gm); |
| try { |
| write(msglist); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| private void setPolicyEcmpGroup(EcmpInfo ecmpInfo) { |
| List<OFMessage> msglist = new ArrayList<OFMessage>(); |
| OFGroup group = OFGroup.of(ecmpInfo.groupId); |
| |
| List<OFBucket> buckets = new ArrayList<OFBucket>(); |
| List<OFAction> actions = new ArrayList<OFAction>(); |
| for (BucketInfo b : ecmpInfo.buckets) { |
| if (b.dstMac != null && b.srcMac != null && b.outport != null) { |
| OFOxmEthDst dmac = factory.oxms() |
| .ethDst(b.dstMac); |
| OFAction setDA = factory.actions().buildSetField() |
| .setField(dmac).build(); |
| OFOxmEthSrc smac = factory.oxms() |
| .ethSrc(b.srcMac); |
| OFAction setSA = factory.actions().buildSetField() |
| .setField(smac).build(); |
| OFAction outp = factory.actions().buildOutput() |
| .setPort(OFPort.of(b.outport.shortValue())) |
| .build(); |
| actions.add(setSA); |
| actions.add(setDA); |
| actions.add(outp); |
| } |
| if (b.groupNo > 0) { |
| OFAction groupTo = factory.actions().buildGroup() |
| .setGroup(OFGroup.of(b.groupNo)) |
| .build(); |
| actions.add(groupTo); |
| } |
| if (b.mplsLabel != -1) { |
| OFAction pushLabel = factory.actions().buildPushMpls() |
| .setEthertype(EthType.MPLS_UNICAST).build(); |
| |
| OFBooleanValue bosValue = null; |
| if (b.bos) |
| bosValue = OFBooleanValue.TRUE; |
| else |
| bosValue = OFBooleanValue.FALSE; |
| OFOxmMplsBos bosX = factory.oxms() |
| .mplsBos(bosValue); |
| OFAction setBX = factory.actions().buildSetField() |
| .setField(bosX).build(); |
| OFOxmMplsLabel lid = factory.oxms() |
| .mplsLabel(U32.of(b.mplsLabel)); |
| OFAction setLabel = factory.actions().buildSetField() |
| .setField(lid).build(); |
| OFAction copyTtl = factory.actions().copyTtlOut(); |
| OFAction decrTtl = factory.actions().decMplsTtl(); |
| actions.add(pushLabel); |
| actions.add(setLabel); |
| actions.add(setBX); |
| actions.add(copyTtl); |
| // decrement TTL only when the first MPLS label is pushed |
| if (b.bos) |
| actions.add(decrTtl); |
| } |
| OFBucket ofb = factory.buildBucket() |
| .setWeight(1) |
| .setActions(actions) |
| .build(); |
| buckets.add(ofb); |
| } |
| |
| OFMessage gm = factory.buildGroupAdd() |
| .setGroup(group) |
| .setBuckets(buckets) |
| .setGroupType(OFGroupType.SELECT) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(gm); |
| try { |
| write(msglist); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| private void deleteGroup(int groupId) { |
| |
| List<OFMessage> msglist = new ArrayList<OFMessage>(); |
| OFGroup group = OFGroup.of(groupId); |
| |
| OFMessage gm = factory.buildGroupDelete() |
| .setGroup(group) |
| .setGroupType(OFGroupType.SELECT) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(gm); |
| try { |
| write(msglist); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| private void modifyEcmpGroup(EcmpInfo ecmpInfo) { |
| List<OFMessage> msglist = new ArrayList<OFMessage>(); |
| OFGroup group = OFGroup.of(ecmpInfo.groupId); |
| |
| List<OFBucket> buckets = new ArrayList<OFBucket>(); |
| for (BucketInfo b : ecmpInfo.buckets) { |
| OFOxmEthDst dmac = factory.oxms() |
| .ethDst(b.dstMac); |
| OFAction setDA = factory.actions().buildSetField() |
| .setField(dmac).build(); |
| OFOxmEthSrc smac = factory.oxms() |
| .ethSrc(b.srcMac); |
| OFAction setSA = factory.actions().buildSetField() |
| .setField(smac).build(); |
| OFAction outp = factory.actions().buildOutput() |
| .setPort(OFPort.of(b.outport.shortValue())) |
| .build(); |
| List<OFAction> actions = new ArrayList<OFAction>(); |
| actions.add(setSA); |
| actions.add(setDA); |
| actions.add(outp); |
| if (b.mplsLabel != -1) { |
| OFAction pushLabel = factory.actions().buildPushMpls() |
| .setEthertype(EthType.MPLS_UNICAST).build(); |
| OFOxmMplsBos bosX = factory.oxms() |
| .mplsBos(OFBooleanValue.TRUE); |
| OFAction setBX = factory.actions().buildSetField() |
| .setField(bosX).build(); |
| OFOxmMplsLabel lid = factory.oxms() |
| .mplsLabel(U32.of(b.mplsLabel)); |
| OFAction setLabel = factory.actions().buildSetField() |
| .setField(lid).build(); |
| OFAction copyTtl = factory.actions().copyTtlOut(); |
| OFAction decrTtl = factory.actions().decMplsTtl(); |
| actions.add(pushLabel); |
| actions.add(setLabel); |
| actions.add(setBX); |
| actions.add(copyTtl); |
| actions.add(decrTtl); |
| } |
| OFBucket ofb = factory.buildBucket() |
| .setWeight(1) |
| .setActions(actions) |
| .build(); |
| buckets.add(ofb); |
| } |
| |
| OFMessage gm = factory.buildGroupModify() |
| .setGroup(group) |
| .setBuckets(buckets) |
| .setGroupType(OFGroupType.SELECT) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(gm); |
| try { |
| write(msglist); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| private void verifyGroups() throws IOException { |
| sendGroupDescRequest(); |
| } |
| |
| private void sendGroupDescRequest() throws IOException { |
| OFMessage gdr = factory.buildGroupDescStatsRequest() |
| .setXid(getNextTransactionId()) |
| .build(); |
| write(gdr, null); |
| } |
| |
| private void assignAdjacencyLabels() { |
| // TODO |
| try { |
| nextDriverState(); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| private OFAction getOFAction(Action action) { |
| OFAction ofAction = null; |
| if (action instanceof OutputAction) { |
| OutputAction outputAction = (OutputAction) action; |
| OFPort port = OFPort.of((int) outputAction.getPortNumber().value()); |
| ofAction = factory.actions().output(port, Short.MAX_VALUE); |
| } else if (action instanceof ModifyDstMacAction) { |
| long dstMac = ((ModifyDstMacAction) action).getDstMac().toLong(); |
| OFOxmEthDst dmac = factory.oxms() |
| .ethDst(MacAddress.of(dstMac)); |
| ofAction = factory.actions().buildSetField() |
| .setField(dmac).build(); |
| } else if (action instanceof ModifySrcMacAction) { |
| long srcMac = ((ModifySrcMacAction) action).getSrcMac().toLong(); |
| OFOxmEthSrc smac = factory.oxms() |
| .ethSrc(MacAddress.of(srcMac)); |
| ofAction = factory.actions().buildSetField() |
| .setField(smac).build(); |
| } else if (action instanceof PushMplsAction) { |
| ofAction = factory.actions().pushMpls(EthType.MPLS_UNICAST); |
| } else if (action instanceof SetMplsIdAction) { |
| int labelid = ((SetMplsIdAction) action).getMplsId(); |
| OFOxmMplsLabel lid = factory.oxms() |
| .mplsLabel(U32.of(labelid)); |
| ofAction = factory.actions().buildSetField() |
| .setField(lid).build(); |
| } else if (action instanceof SetMplsBosAction) { |
| OFBooleanValue val = OFBooleanValue.of( |
| ((SetMplsBosAction) action).isSet()); |
| OFOxmMplsBos bos = factory.oxms().mplsBos(val); |
| OFAction setBos = factory.actions().buildSetField() |
| .setField(bos).build(); |
| } else if (action instanceof PopMplsAction) { |
| EthType ethertype = ((PopMplsAction) action).getEthType(); |
| ofAction = factory.actions().popMpls(ethertype); |
| } else if (action instanceof GroupAction) { |
| int gid = -1; |
| GroupAction ga = (GroupAction)action; |
| if (ga.getTunnelId() != null) { |
| List<Integer> groupIds = tunnelGroupIdTable.get(ga.getTunnelId()); |
| gid = groupIds.get(groupIds.size()-1); |
| } |
| else { |
| NeighborSet ns = ((GroupAction) action).getDpids(); |
| EcmpInfo ei = ecmpGroups.get(ns); |
| if (ei == null) { |
| log.debug("Unable to find ecmp group for neighbors {} at " |
| + "switch {} and hence creating it", ns, getStringId()); |
| createGroupForANeighborSet(ns, groupid.incrementAndGet()); |
| ei = ecmpGroups.get(ns); |
| } |
| gid = ei.groupId; |
| } |
| ofAction = factory.actions().buildGroup() |
| .setGroup(OFGroup.of(gid)) |
| .build(); |
| } else if (action instanceof DecNwTtlAction) { |
| ofAction = factory.actions().decNwTtl(); |
| } else if (action instanceof DecMplsTtlAction) { |
| ofAction = factory.actions().decMplsTtl(); |
| } else if (action instanceof CopyTtlInAction) { |
| ofAction = factory.actions().copyTtlIn(); |
| } else if (action instanceof CopyTtlOutAction) { |
| ofAction = factory.actions().copyTtlOut(); |
| } else { |
| log.warn("Unsupported Action type: {}", action.getClass().getName()); |
| return null; |
| } |
| |
| return ofAction; |
| } |
| |
| private OFMessage getIpEntry(MatchActionOperationEntry mao) { |
| MatchAction ma = mao.getTarget(); |
| Operator op = mao.getOperator(); |
| Ipv4Match ipm = (Ipv4Match) ma.getMatch(); |
| |
| // set match |
| IPv4Net ipdst = ipm.getDestination(); |
| OFOxmEthType ethTypeIp = factory.oxms() |
| .ethType(EthType.IPv4); |
| OFOxmIpv4DstMasked ipPrefix = factory.oxms() |
| .ipv4DstMasked( |
| IPv4Address.of(ipdst.address().value()), |
| IPv4Address.ofCidrMaskLength(ipdst.prefixLen()) |
| ); |
| OFOxmList oxmList = OFOxmList.of(ethTypeIp, ipPrefix); |
| OFMatchV3 match = factory.buildMatchV3() |
| .setOxmList(oxmList).build(); |
| |
| // set actions |
| List<OFAction> writeActions = new ArrayList<OFAction>(); |
| for (Action action : ma.getActions()) { |
| OFAction ofAction = getOFAction(action); |
| if (ofAction != null) { |
| writeActions.add(ofAction); |
| } |
| } |
| |
| // set instructions |
| OFInstruction writeInstr = factory.instructions().buildWriteActions() |
| .setActions(writeActions).build(); |
| OFInstruction gotoInstr = factory.instructions().buildGotoTable() |
| .setTableId(TableId.of(TABLE_ACL)).build(); |
| List<OFInstruction> instructions = new ArrayList<OFInstruction>(); |
| instructions.add(writeInstr); |
| instructions.add(gotoInstr); |
| |
| // set flow priority to emulate longest prefix match |
| int priority = ipdst.prefixLen() * PRIORITY_MULTIPLIER; |
| if (ipdst.prefixLen() == (short) 32) { |
| priority = MAX_PRIORITY; |
| } |
| |
| // set flow-mod |
| OFFlowMod.Builder fmBuilder = null; |
| switch (op) { |
| case ADD: |
| fmBuilder = factory.buildFlowAdd(); |
| break; |
| case REMOVE: |
| fmBuilder = factory.buildFlowDeleteStrict(); |
| break; |
| case MODIFY: // TODO |
| fmBuilder = factory.buildFlowModifyStrict(); |
| break; |
| default: |
| log.warn("Unsupported MatchAction Operator: {}", op); |
| return null; |
| } |
| OFMessage ipFlow = fmBuilder |
| .setTableId(TableId.of(TABLE_IPv4_UNICAST)) |
| .setMatch(match) |
| .setInstructions(instructions) |
| .setPriority(priority) |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| log.debug("{} ip-rule {}-{} in sw {}", |
| (op == MatchActionOperations.Operator.ADD) ? "Adding" : "Deleting", |
| match, writeActions, |
| getStringId()); |
| return ipFlow; |
| } |
| |
| private OFMessage getMplsEntry(MatchActionOperationEntry mao) { |
| MatchAction ma = mao.getTarget(); |
| Operator op = mao.getOperator(); |
| MplsMatch mplsm = (MplsMatch) ma.getMatch(); |
| |
| // set match |
| OFOxmEthType ethTypeMpls = factory.oxms() |
| .ethType(EthType.MPLS_UNICAST); |
| OFOxmMplsLabel labelid = factory.oxms() |
| .mplsLabel(U32.of(mplsm.getMplsLabel())); |
| OFOxmMplsBos bos = factory.oxms() |
| .mplsBos(OFBooleanValue.of(mplsm.isBos())); |
| OFOxmList oxmList = OFOxmList.of(ethTypeMpls, labelid, bos); |
| OFMatchV3 matchlabel = factory.buildMatchV3() |
| .setOxmList(oxmList).build(); |
| |
| // set actions |
| List<OFAction> writeActions = new ArrayList<OFAction>(); |
| for (Action action : ma.getActions()) { |
| OFAction ofAction = getOFAction(action); |
| if (ofAction != null) { |
| writeActions.add(ofAction); |
| } |
| } |
| |
| // set instructions |
| OFInstruction writeInstr = factory.instructions().buildWriteActions() |
| .setActions(writeActions).build(); |
| OFInstruction gotoInstr = factory.instructions().buildGotoTable() |
| .setTableId(TableId.of(TABLE_ACL)).build(); |
| List<OFInstruction> instructions = new ArrayList<OFInstruction>(); |
| instructions.add(writeInstr); |
| instructions.add(gotoInstr); |
| |
| // set flow-mod |
| OFFlowMod.Builder fmBuilder = null; |
| switch (op) { |
| case ADD: |
| fmBuilder = factory.buildFlowAdd(); |
| break; |
| case REMOVE: |
| fmBuilder = factory.buildFlowDeleteStrict(); |
| break; |
| case MODIFY: // TODO |
| fmBuilder = factory.buildFlowModifyStrict(); |
| break; |
| default: |
| log.warn("Unsupported MatchAction Operator: {}", op); |
| return null; |
| } |
| |
| OFMessage mplsFlow = fmBuilder |
| .setTableId(TableId.of(TABLE_MPLS)) |
| .setMatch(matchlabel) |
| .setInstructions(instructions) |
| .setPriority(MAX_PRIORITY) // exact match and exclusive |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| log.debug("{} mpls-rule {}-{} in sw {}", |
| (op == MatchActionOperations.Operator.ADD) ? "Adding" : "Deleting", |
| matchlabel, writeActions, |
| getStringId()); |
| return mplsFlow; |
| } |
| |
| private OFMessage getAclEntry(MatchActionOperationEntry mao) { |
| MatchAction ma = mao.getTarget(); |
| Operator op = mao.getOperator(); |
| PacketMatch packetMatch = (PacketMatch) ma.getMatch(); |
| Builder matchBuilder = factory.buildMatch(); |
| |
| // set match |
| int inport = 0; |
| if (ma.getSwitchPort() != null) { |
| inport = (int) ma.getSwitchPort().getPortNumber().value(); |
| } |
| final MACAddress srcMac = packetMatch.getSrcMacAddress(); |
| final MACAddress dstMac = packetMatch.getDstMacAddress(); |
| final Short etherType = packetMatch.getEtherType(); |
| final IPv4Net srcIp = packetMatch.getSrcIpAddress(); |
| final IPv4Net dstIp = packetMatch.getDstIpAddress(); |
| final Byte ipProto = packetMatch.getIpProtocolNumber(); |
| final Short srcTcpPort = packetMatch.getSrcTcpPortNumber(); |
| final Short dstTcpPort = packetMatch.getDstTcpPortNumber(); |
| if (inport > 0) { |
| matchBuilder.setExact(MatchField.IN_PORT, |
| OFPort.of(inport)); |
| } |
| if (srcMac != null) { |
| matchBuilder.setExact(MatchField.ETH_SRC, MacAddress.of(srcMac.toLong())); |
| } |
| if (dstMac != null) { |
| matchBuilder.setExact(MatchField.ETH_DST, MacAddress.of(dstMac.toLong())); |
| } |
| if (etherType != null) { |
| matchBuilder.setExact(MatchField.ETH_TYPE, EthType.of(etherType)); |
| } |
| if (srcIp != null) { |
| matchBuilder.setMasked(MatchField.IPV4_SRC, |
| IPv4Address.of(srcIp.address().value()) |
| .withMaskOfLength(srcIp.prefixLen())); |
| } |
| if (dstIp != null) { |
| matchBuilder.setMasked(MatchField.IPV4_DST, |
| IPv4Address.of(dstIp.address().value()) |
| .withMaskOfLength(dstIp.prefixLen())); |
| } |
| if (ipProto != null) { |
| matchBuilder.setExact(MatchField.IP_PROTO, IpProtocol.of(ipProto)); |
| } |
| if (srcTcpPort != null) { |
| matchBuilder.setExact(MatchField.TCP_SRC, TransportPort.of(srcTcpPort)); |
| } |
| if (dstTcpPort != null) { |
| matchBuilder.setExact(MatchField.TCP_DST, TransportPort.of(dstTcpPort)); |
| } |
| |
| // set actions |
| List<OFAction> writeActions = new ArrayList<OFAction>(); |
| for (Action action : ma.getActions()) { |
| OFAction ofAction = getOFAction(action); |
| if (ofAction != null) { |
| writeActions.add(ofAction); |
| } |
| } |
| |
| // set instructions |
| OFInstruction clearInstr = factory.instructions().clearActions(); |
| OFInstruction writeInstr = factory.instructions().buildWriteActions() |
| .setActions(writeActions).build(); |
| List<OFInstruction> instructions = new ArrayList<OFInstruction>(); |
| instructions.add(clearInstr); |
| instructions.add(writeInstr); |
| |
| // set flow-mod |
| OFFlowMod.Builder fmBuilder = null; |
| switch (op) { |
| case ADD: |
| fmBuilder = factory.buildFlowAdd(); |
| break; |
| case REMOVE: |
| fmBuilder = factory.buildFlowDeleteStrict(); |
| break; |
| case MODIFY: // TODO |
| fmBuilder = factory.buildFlowModifyStrict(); |
| break; |
| default: |
| log.warn("Unsupported MatchAction Operator: {}", op); |
| return null; |
| } |
| |
| OFMessage aclFlow = fmBuilder |
| .setTableId(TableId.of(TABLE_ACL)) |
| .setMatch(matchBuilder.build()) |
| .setInstructions(instructions) |
| .setPriority(ma.getPriority()) // exact match and exclusive |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| |
| return aclFlow; |
| } |
| |
| // ***************************** |
| // IOF13Switch |
| // ***************************** |
| |
| @Override |
| public void pushFlow(MatchActionOperationEntry matchActionOp) throws IOException { |
| OFMessage ofm = getFlow(matchActionOp); |
| if (ofm != null) { |
| write(Collections.singletonList(ofm)); |
| } |
| } |
| |
| private OFMessage getFlow(MatchActionOperationEntry matchActionOp) { |
| final MatchAction matchAction = matchActionOp.getTarget(); |
| final Match match = matchAction.getMatch(); |
| if (match instanceof Ipv4Match) { |
| return getIpEntry(matchActionOp); |
| } else if (match instanceof MplsMatch) { |
| return getMplsEntry(matchActionOp); |
| } else if (match instanceof PacketMatch) { |
| return getAclEntry(matchActionOp); |
| } else { |
| log.error("Unknown match type {} pushed to switch {}", match, |
| getStringId()); |
| } |
| return null; |
| } |
| |
| @Override |
| public void pushFlows(Collection<MatchActionOperationEntry> matchActionOps) |
| throws IOException { |
| List<OFMessage> flowMods = new ArrayList<OFMessage>(); |
| for (MatchActionOperationEntry matchActionOp : matchActionOps) { |
| OFMessage ofm = getFlow(matchActionOp); |
| if (ofm != null) { |
| flowMods.add(ofm); |
| } |
| } |
| write(flowMods); |
| } |
| |
| @Override |
| public int getEcmpGroupId(NeighborSet ns) { |
| EcmpInfo ei = ecmpGroups.get(ns); |
| if (ei == null) { |
| return -1; |
| } else { |
| return ei.groupId; |
| } |
| } |
| |
| @Override |
| public TableId getTableId(String tableType) { |
| tableType = tableType.toLowerCase(); |
| if (tableType.contentEquals("ip")) { |
| return TableId.of(OFSwitchImplCPqD13.TABLE_IPv4_UNICAST); |
| } |
| else if (tableType.contentEquals("mpls")) { |
| return TableId.of(OFSwitchImplCPqD13.TABLE_MPLS); |
| } |
| else if (tableType.contentEquals("acl")) { |
| return TableId.of(OFSwitchImplCPqD13.TABLE_ACL); |
| } |
| else { |
| log.warn("Invalid tableType: {}", tableType); |
| return null; |
| } |
| } |
| |
| @Override |
| public void createTunnel(String tunnelId, List<String> route, NeighborSet ns) { |
| |
| List<Integer> groups = new ArrayList<Integer>(); |
| |
| // create a last group of the group chaining |
| int finalGroupId = groupid.incrementAndGet(); |
| createGroupForANeighborSet(ns, finalGroupId); |
| groups.add(Integer.valueOf(finalGroupId)); |
| |
| int groupId = 0; |
| int nextGroupId = finalGroupId; |
| boolean bos = false; |
| |
| // process the node ID in order |
| for (int i = 0; i < route.size(); i++) { |
| String nodeId = route.get(i); |
| groupId = groupid.incrementAndGet(); |
| groups.add(Integer.valueOf(groupId)); |
| if (i == route.size()-1) |
| bos = true; |
| createGroupForMplsLabel(groupId, nodeId, nextGroupId, bos); |
| nextGroupId = groupId; |
| } |
| tunnelGroupIdTable.putIfAbsent(tunnelId, groups); |
| } |
| |
| @Override |
| public void removeTunnel(String tunnelId) { |
| List<Integer> groups = tunnelGroupIdTable.get(tunnelId); |
| if (groups == null) |
| return; |
| |
| // we need to delete groups in reverse order |
| for (int i = groups.size() - 1; i >= 0; i--) { |
| int groupId = groups.get(i); |
| deleteGroup(groupId); |
| } |
| tunnelGroupIdTable.remove(tunnelId); |
| } |
| |
| @Override |
| public int getTunnelGroupId(String tunnelId) { |
| List<Integer> groups = tunnelGroupIdTable.get(tunnelId); |
| if (groups == null) |
| return -1; |
| |
| return groups.get(0); |
| } |
| |
| |
| // ***************************** |
| // Unused |
| // ***************************** |
| |
| @SuppressWarnings("unused") |
| private void setAsyncConfig() throws IOException { |
| List<OFMessage> msglist = new ArrayList<OFMessage>(3); |
| OFMessage setAC = null; |
| |
| if (role == Role.MASTER) { |
| setAC = factory.buildAsyncSet() |
| .setFlowRemovedMaskEqualMaster(SET_FLOW_REMOVED_MASK_MASTER) |
| .setPacketInMaskEqualMaster(SET_PACKET_IN_MASK_MASTER) |
| .setPortStatusMaskEqualMaster(SET_PORT_STATUS_MASK_MASTER) |
| .setFlowRemovedMaskSlave(SET_ALL_SLAVE) |
| .setPacketInMaskSlave(SET_ALL_SLAVE) |
| .setPortStatusMaskSlave(SET_ALL_SLAVE) |
| .setXid(getNextTransactionId()) |
| .build(); |
| } else if (role == Role.EQUAL) { |
| setAC = factory.buildAsyncSet() |
| .setFlowRemovedMaskEqualMaster(SET_FLOW_REMOVED_MASK_EQUAL) |
| .setPacketInMaskEqualMaster(SET_PACKET_IN_MASK_EQUAL) |
| .setPortStatusMaskEqualMaster(SET_PORT_STATUS_MASK_EQUAL) |
| .setFlowRemovedMaskSlave(SET_ALL_SLAVE) |
| .setPacketInMaskSlave(SET_ALL_SLAVE) |
| .setPortStatusMaskSlave(SET_ALL_SLAVE) |
| .setXid(getNextTransactionId()) |
| .build(); |
| } |
| msglist.add(setAC); |
| |
| OFMessage br = factory.buildBarrierRequest() |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(br); |
| |
| OFMessage getAC = factory.buildAsyncGetRequest() |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(getAC); |
| |
| write(msglist); |
| } |
| |
| @SuppressWarnings("unused") |
| private void decodeAsyncGetReply(OFAsyncGetReply rep) { |
| long frm = rep.getFlowRemovedMaskEqualMaster(); |
| long frs = rep.getFlowRemovedMaskSlave(); |
| long pim = rep.getPacketInMaskEqualMaster(); |
| long pis = rep.getPacketInMaskSlave(); |
| long psm = rep.getPortStatusMaskEqualMaster(); |
| long pss = rep.getPortStatusMaskSlave(); |
| |
| if (role == Role.MASTER || role == Role.EQUAL) { // should separate |
| log.info("FRM:{}", HexString.toHexString((frm & TEST_FLOW_REMOVED_MASK))); |
| log.info("PIM:{}", HexString.toHexString((pim & TEST_PACKET_IN_MASK))); |
| log.info("PSM:{}", HexString.toHexString((psm & TEST_PORT_STATUS_MASK))); |
| } |
| |
| } |
| |
| @SuppressWarnings("unused") |
| private void getTableFeatures() throws IOException { |
| OFMessage gtf = factory.buildTableFeaturesStatsRequest() |
| .setXid(getNextTransactionId()) |
| .build(); |
| write(gtf, null); |
| } |
| |
| @SuppressWarnings("unused") |
| private void sendGroupFeaturesRequest() throws IOException { |
| OFMessage gfr = factory.buildGroupFeaturesStatsRequest() |
| .setXid(getNextTransactionId()) |
| .build(); |
| write(gfr, null); |
| } |
| |
| private void processGroupFeatures(OFGroupFeaturesStatsReply gfsr) { |
| log.info("Sw: {} Group Features {}", getStringId(), gfsr); |
| } |
| |
| @SuppressWarnings("unused") |
| private void testMultipleLabels() { |
| if (getId() == 1) { |
| List<OFMessage> msglist = new ArrayList<OFMessage>(); |
| |
| // first all the indirect groups |
| |
| // the group to switch 2 with outer label |
| OFGroup g1 = OFGroup.of(201); |
| OFOxmEthDst dmac1 = factory.oxms().ethDst(MacAddress.of("00:00:02:02:02:80")); |
| OFAction push1 = factory.actions().pushMpls(EthType.MPLS_UNICAST); |
| OFOxmMplsLabel lid1 = factory.oxms() |
| .mplsLabel(U32.of(105)); // outer label |
| OFAction setMpls1 = factory.actions().buildSetField() |
| .setField(lid1).build(); |
| OFOxmMplsBos bos1 = factory.oxms() |
| .mplsBos(OFBooleanValue.FALSE); |
| OFAction setB1 = factory.actions().buildSetField() |
| .setField(bos1).build(); |
| OFAction setDA1 = factory.actions().buildSetField() |
| .setField(dmac1).build(); |
| OFAction outp1 = factory.actions().buildOutput() |
| .setPort(OFPort.of(2)) |
| .build(); |
| List<OFAction> a1 = new ArrayList<OFAction>(); |
| a1.add(push1); |
| a1.add(setMpls1); |
| a1.add(setB1); |
| a1.add(setDA1); |
| a1.add(outp1); |
| OFBucket b1 = factory.buildBucket() |
| .setActions(a1) |
| .build(); |
| OFMessage gm1 = factory.buildGroupAdd() |
| .setGroup(g1) |
| .setBuckets(Collections.singletonList(b1)) |
| .setGroupType(OFGroupType.INDIRECT) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(gm1); |
| |
| // the group to switch 3 with outer label |
| OFGroup g2 = OFGroup.of(301); |
| OFOxmEthDst dmac2 = factory.oxms().ethDst(MacAddress.of("00:00:03:03:03:80")); |
| OFAction push2 = factory.actions().pushMpls(EthType.MPLS_UNICAST); |
| OFOxmMplsLabel lid2 = factory.oxms() |
| .mplsLabel(U32.of(104)); // outer label |
| OFAction setMpls2 = factory.actions().buildSetField() |
| .setField(lid2).build(); |
| OFOxmMplsBos bos2 = factory.oxms() |
| .mplsBos(OFBooleanValue.FALSE); |
| OFAction setB2 = factory.actions().buildSetField() |
| .setField(bos2).build(); |
| OFAction setDA2 = factory.actions().buildSetField() |
| .setField(dmac2).build(); |
| OFAction outp2 = factory.actions().buildOutput() |
| .setPort(OFPort.of(3)) |
| .build(); |
| List<OFAction> a2 = new ArrayList<OFAction>(); |
| a2.add(push2); |
| a2.add(setMpls2); |
| a2.add(setB2); |
| a2.add(setDA2); |
| a2.add(outp2); |
| OFBucket b2 = factory.buildBucket() |
| .setActions(a2) |
| .build(); |
| OFMessage gm2 = factory.buildGroupAdd() |
| .setGroup(g2) |
| .setBuckets(Collections.singletonList(b2)) |
| .setGroupType(OFGroupType.INDIRECT) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(gm2); |
| |
| // now add main ECMP group with inner labels |
| OFGroup group = OFGroup.of(786); |
| List<OFBucket> buckets = new ArrayList<OFBucket>(); |
| for (int i = 0; i < 2; i++) { // 2 buckets |
| |
| List<OFAction> actions = new ArrayList<OFAction>(); |
| OFOxmEthSrc smac = factory.oxms() |
| .ethSrc(MacAddress.of("00:00:01:01:01:80")); |
| OFAction setSA = factory.actions().buildSetField() |
| .setField(smac).build(); |
| actions.add(setSA); |
| |
| if (i == 0) { |
| // send to switch 2 |
| OFAction pushX = factory.actions().pushMpls(EthType.MPLS_UNICAST); |
| OFOxmMplsLabel lidX = factory.oxms() |
| .mplsLabel(U32.of(106)); // inner label |
| OFAction setX = factory.actions().buildSetField() |
| .setField(lidX).build(); |
| OFOxmMplsBos bosX = factory.oxms() |
| .mplsBos(OFBooleanValue.TRUE); |
| OFAction setBX = factory.actions().buildSetField() |
| .setField(bosX).build(); |
| OFAction ogX = factory.actions().buildGroup() |
| .setGroup(g1).build(); |
| actions.add(pushX); |
| actions.add(setX); |
| actions.add(setBX); |
| actions.add(ogX); |
| |
| } else { |
| // send to switch 3 |
| OFAction pushY = factory.actions().pushMpls(EthType.MPLS_UNICAST); |
| OFOxmMplsLabel lidY = factory.oxms() |
| .mplsLabel(U32.of(106)); // inner label |
| OFAction setY = factory.actions().buildSetField() |
| .setField(lidY).build(); |
| OFOxmMplsBos bosY = factory.oxms() |
| .mplsBos(OFBooleanValue.TRUE); |
| OFAction setBY = factory.actions().buildSetField() |
| .setField(bosY).build(); |
| OFAction ogY = factory.actions().buildGroup() |
| .setGroup(g2).build(); |
| actions.add(pushY); |
| actions.add(setY); |
| actions.add(setBY); |
| actions.add(ogY); |
| } |
| |
| OFBucket ofb = factory.buildBucket() |
| .setWeight(1) |
| .setActions(actions) |
| .build(); |
| buckets.add(ofb); |
| } |
| |
| OFMessage gm = factory.buildGroupAdd() |
| .setGroup(group) |
| .setBuckets(buckets) |
| .setGroupType(OFGroupType.SELECT) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(gm); |
| |
| // create an ACL entry to use this ecmp group |
| Builder matchBuilder = factory.buildMatch(); |
| matchBuilder.setExact(MatchField.ETH_TYPE, EthType.of(0x800)); |
| matchBuilder.setMasked(MatchField.IPV4_DST, |
| IPv4Address.of("7.7.7.0") |
| .withMaskOfLength(24)); |
| |
| OFAction grp = factory.actions().buildGroup() |
| .setGroup(OFGroup.of(786)) |
| .build(); |
| List<OFAction> writeActions = Collections.singletonList(grp); |
| |
| OFInstruction clearInstr = factory.instructions().clearActions(); |
| OFInstruction writeInstr = factory.instructions().buildWriteActions() |
| .setActions(writeActions).build(); |
| List<OFInstruction> instructions = new ArrayList<OFInstruction>(); |
| instructions.add(clearInstr); |
| instructions.add(writeInstr); |
| |
| OFFlowMod.Builder fmBuilder = factory.buildFlowAdd(); |
| |
| OFMessage aclFlow = fmBuilder |
| .setTableId(TableId.of(TABLE_ACL)) |
| .setMatch(matchBuilder.build()) |
| .setInstructions(instructions) |
| .setPriority(10) // TODO: wrong - should be MA |
| // priority |
| .setBufferId(OFBufferId.NO_BUFFER) |
| .setIdleTimeout(0) |
| .setHardTimeout(0) |
| .setXid(getNextTransactionId()) |
| .build(); |
| msglist.add(aclFlow); |
| |
| try { |
| write(msglist); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| } |