org.sdntest.app.SDNTest.java Source code

Java tutorial

Introduction

Here is the source code for org.sdntest.app.SDNTest.java

Source

/*
 * Copyright 2014-2015 Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
 *
 * 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.sdntest.app;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ARP;
import org.onlab.packet.IpAddress;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.ICMP;
import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.Ip6Prefix;
import org.onlab.packet.TCP;
import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultPath;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.edge.EdgePortService;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.host.HostService;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.host.PortAddresses;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.DeviceId;
import org.osgi.service.component.ComponentContext;
import org.onosproject.net.resource.link.BandwidthResourceRequest;
import org.onosproject.net.resource.link.LinkResourceService;
import org.onosproject.net.resource.ResourceRequest;
import org.onosproject.net.resource.ResourceType;

import org.onosproject.net.topology.DefaultTopologyVertex;
import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.topology.TopologyVertex;

import org.onlab.graph.Edge;
import org.onlab.graph.EdgeWeight;
import org.onlab.graph.GraphPathSearch;
import org.onlab.graph.GraphPathSearch.Result;
import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.common.DefaultTopology;
import org.onosproject.net.proxyarp.ProxyArpService;
import com.google.common.collect.ImmutableSet;

import java.util.Dictionary;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.slf4j.LoggerFactory.getLogger;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;

/**
 * Sample forwarding application modified for vlan testing.
 */
@Component(immediate = true)
public class SDNTest {

    private static final BWGraphSearch<TopologyVertex, TopologyEdge> BWSEARCH = new BWGraphSearch<>();

    private static final int DEFAULT_TIMEOUT = 10;
    private static final int DEFAULT_PRIORITY = 10;
    private static final String NOT_ARP_REQUEST = "ARP is not a request.";
    private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";

    private final org.slf4j.Logger log = getLogger(getClass());

    private static final String GLOBUS_LOGIN_FILE = "/home/sdn/gllogin";
    private static final String GLOBUS_REST_ROOT_URL = "https://transfer.api.globusonline.org/v0.10/";
    private static final String GLOBUS_AUTH_URL = "https://nexus.api.globusonline.org/goauth/token?grant_type=client_credentials";

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected EdgePortService edgeService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected ProxyArpService proxyArpService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected TopologyService topologyService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected DeviceService deviceService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected PacketService packetService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected HostService hostService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected FlowRuleService flowRuleService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected FlowObjectiveService flowObjectiveService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected CoreService coreService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected ComponentConfigService cfgService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected LinkResourceService resourceService;

    private SDNTPacketProcessor processor = new SDNTPacketProcessor();

    private ApplicationId appId;

    @Property(name = "packetOutOnly", boolValue = false, label = "Enable packet-out only forwarding; default is false")
    private boolean packetOutOnly = false;

    @Property(name = "packetOutOfppTable", boolValue = false, label = "Enable first packet forwarding using OFPP_TABLE port "
            + "instead of PacketOut with actual port; default is false")
    private boolean packetOutOfppTable = false;

    @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT, label = "Configure Flow Timeout for installed flow rules; "
            + "default is 10 sec")
    private int flowTimeout = DEFAULT_TIMEOUT;

    @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY, label = "Configure Flow Priority for installed flow rules; "
            + "default is 10")
    private int flowPriority = DEFAULT_PRIORITY;

    @Property(name = "ipv6Forwarding", boolValue = false, label = "Enable IPv6 forwarding; default is false")
    private boolean ipv6Forwarding = false;

    @Property(name = "matchDstMacOnly", boolValue = false, label = "Enable matching Dst Mac Only; default is false")
    private boolean matchDstMacOnly = false;

    @Property(name = "matchVlanId", boolValue = false, label = "Enable matching Vlan ID; default is false")
    private boolean matchVlanId = false;

    @Property(name = "matchIpv4Address", boolValue = false, label = "Enable matching IPv4 Addresses; default is false")
    private boolean matchIpv4Address = false;

    @Property(name = "matchIpv4Dscp", boolValue = false, label = "Enable matching IPv4 DSCP and ECN; default is false")
    private boolean matchIpv4Dscp = false;

    @Property(name = "matchIpv6Address", boolValue = false, label = "Enable matching IPv6 Addresses; default is false")
    private boolean matchIpv6Address = false;

    @Property(name = "matchIpv6FlowLabel", boolValue = false, label = "Enable matching IPv6 FlowLabel; default is false")
    private boolean matchIpv6FlowLabel = false;

    @Property(name = "matchTcpUdpPorts", boolValue = false, label = "Enable matching TCP/UDP ports; default is false")
    private boolean matchTcpUdpPorts = false;

    @Property(name = "matchIcmpFields", boolValue = false, label = "Enable matching ICMPv4 and ICMPv6 fields; "
            + "default is false")
    private boolean matchIcmpFields = false;

    private Map<String, Map<String, Map<Short, Short>>> vlanDstIpMap;
    private Map<String, Map<String, Map<Short, Short>>> vlanTransMacMap;
    private Map<String, Map<String, Map<Short, Short>>> vlanDstMacMap;

    private void initVlanMap() {

        vlanDstIpMap = new HashMap<String, Map<String, Map<Short, Short>>>();
        vlanDstIpMap.put("00:00:00:00:00:01", new HashMap<String, Map<Short, Short>>());
        vlanDstIpMap.get("00:00:00:00:00:01").put("192.168.1.3", new HashMap<Short, Short>());
        vlanDstIpMap.get("00:00:00:00:00:01").get("192.168.1.3").put((short) 5, (short) 10);

        vlanDstIpMap.put("00:00:00:00:00:02", new HashMap<String, Map<Short, Short>>());
        vlanDstIpMap.get("00:00:00:00:00:02").put("192.168.1.2", new HashMap<Short, Short>());
        vlanDstIpMap.get("00:00:00:00:00:02").get("192.168.1.2").put((short) 10, (short) 5);

        vlanDstMacMap = new HashMap<String, Map<String, Map<Short, Short>>>();
        vlanDstMacMap.put("00:00:00:00:00:01", new HashMap<String, Map<Short, Short>>());
        vlanDstMacMap.get("00:00:00:00:00:01").put("00:00:00:00:00:02", new HashMap<Short, Short>());
        vlanDstMacMap.get("00:00:00:00:00:01").get("00:00:00:00:00:02").put((short) 7, (short) 10);

        vlanDstMacMap.put("00:00:00:00:00:02", new HashMap<String, Map<Short, Short>>());
        vlanDstMacMap.get("00:00:00:00:00:02").put("00:00:00:00:00:01", new HashMap<Short, Short>());
        vlanDstMacMap.get("00:00:00:00:00:02").get("00:00:00:00:00:01").put((short) 7, (short) 5);

        vlanTransMacMap = new HashMap<String, Map<String, Map<Short, Short>>>();
        vlanTransMacMap.put("00:00:00:00:00:01", new HashMap<String, Map<Short, Short>>());
        vlanTransMacMap.get("00:00:00:00:00:01").put("00:00:00:00:00:02", new HashMap<Short, Short>());
        vlanTransMacMap.get("00:00:00:00:00:01").get("00:00:00:00:00:02").put((short) 5, (short) 7);

        vlanTransMacMap.put("00:00:00:00:00:02", new HashMap<String, Map<Short, Short>>());
        vlanTransMacMap.get("00:00:00:00:00:02").put("00:00:00:00:00:01", new HashMap<Short, Short>());
        vlanTransMacMap.get("00:00:00:00:00:02").get("00:00:00:00:00:01").put((short) 10, (short) 7);
    }

    @Activate
    public void activate(ComponentContext context) {
        initVlanMap();
        cfgService.registerProperties(getClass());
        appId = coreService.registerApplication("org.sdntest.app");

        packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
        readComponentConfiguration(context);
        requestIntercepts();
        addDefaultRules();

        log.info("Started with Application ID {}", appId.id());
    }

    @Deactivate
    public void deactivate() {
        cfgService.unregisterProperties(getClass(), false);
        removeDefaultRules();
        withdrawIntercepts();
        flowRuleService.removeFlowRulesById(appId);
        packetService.removeProcessor(processor);
        processor = null;
        log.info("Stopped");
    }

    @Modified
    public void modified(ComponentContext context) {
        readComponentConfiguration(context);
        requestIntercepts();
    }

    /**
     * Request packet in via PacketService.
     */
    private void requestIntercepts() {
        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
        selector.matchEthType(Ethernet.TYPE_IPV4);
        packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
        selector.matchEthType(Ethernet.TYPE_ARP);
        packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
        selector.matchEthType(Ethernet.TYPE_IPV6);
        packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
    }

    /**
     * Request packet in via PacketService.
     */
    private void withdrawIntercepts() {
        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
        selector.matchEthType(Ethernet.TYPE_IPV4);
        packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
        selector.matchEthType(Ethernet.TYPE_ARP);
        packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
        selector.matchEthType(Ethernet.TYPE_IPV6);
        packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
    }

    // init known rules as needed
    private void addDefaultRules() {
        return;
    }

    private void removeDefaultRules() {
        return;
    }

    /**
     * Extracts properties from the component configuration context.
     *
     * @param context the component context
     */
    private void readComponentConfiguration(ComponentContext context) {
        Dictionary<?, ?> properties = context.getProperties();
        boolean packetOutOnlyEnabled = isPropertyEnabled(properties, "packetOutOnly");
        if (packetOutOnly != packetOutOnlyEnabled) {
            packetOutOnly = packetOutOnlyEnabled;
            log.info("Configured. Packet-out only forwarding is {}", packetOutOnly ? "enabled" : "disabled");
        }
        boolean packetOutOfppTableEnabled = isPropertyEnabled(properties, "packetOutOfppTable");
        if (packetOutOfppTable != packetOutOfppTableEnabled) {
            packetOutOfppTable = packetOutOfppTableEnabled;
            log.info("Configured. Forwarding using OFPP_TABLE port is {}",
                    packetOutOfppTable ? "enabled" : "disabled");
        }
        boolean ipv6ForwardingEnabled = isPropertyEnabled(properties, "ipv6Forwarding");
        if (ipv6Forwarding != ipv6ForwardingEnabled) {
            ipv6Forwarding = ipv6ForwardingEnabled;
            log.info("Configured. IPv6 forwarding is {}", ipv6Forwarding ? "enabled" : "disabled");
        }
        boolean matchDstMacOnlyEnabled = isPropertyEnabled(properties, "matchDstMacOnly");
        if (matchDstMacOnly != matchDstMacOnlyEnabled) {
            matchDstMacOnly = matchDstMacOnlyEnabled;
            log.info("Configured. Match Dst MAC Only is {}", matchDstMacOnly ? "enabled" : "disabled");
        }
        boolean matchVlanIdEnabled = isPropertyEnabled(properties, "matchVlanId");
        if (matchVlanId != matchVlanIdEnabled) {
            matchVlanId = matchVlanIdEnabled;
            log.info("Configured. Matching Vlan ID is {}", matchVlanId ? "enabled" : "disabled");
        }
        boolean matchIpv4AddressEnabled = isPropertyEnabled(properties, "matchIpv4Address");
        if (matchIpv4Address != matchIpv4AddressEnabled) {
            matchIpv4Address = matchIpv4AddressEnabled;
            log.info("Configured. Matching IPv4 Addresses is {}", matchIpv4Address ? "enabled" : "disabled");
        }
        boolean matchIpv4DscpEnabled = isPropertyEnabled(properties, "matchIpv4Dscp");
        if (matchIpv4Dscp != matchIpv4DscpEnabled) {
            matchIpv4Dscp = matchIpv4DscpEnabled;
            log.info("Configured. Matching IPv4 DSCP and ECN is {}", matchIpv4Dscp ? "enabled" : "disabled");
        }
        boolean matchIpv6AddressEnabled = isPropertyEnabled(properties, "matchIpv6Address");
        if (matchIpv6Address != matchIpv6AddressEnabled) {
            matchIpv6Address = matchIpv6AddressEnabled;
            log.info("Configured. Matching IPv6 Addresses is {}", matchIpv6Address ? "enabled" : "disabled");
        }
        boolean matchIpv6FlowLabelEnabled = isPropertyEnabled(properties, "matchIpv6FlowLabel");
        if (matchIpv6FlowLabel != matchIpv6FlowLabelEnabled) {
            matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
            log.info("Configured. Matching IPv6 FlowLabel is {}", matchIpv6FlowLabel ? "enabled" : "disabled");
        }
        boolean matchTcpUdpPortsEnabled = isPropertyEnabled(properties, "matchTcpUdpPorts");
        if (matchTcpUdpPorts != matchTcpUdpPortsEnabled) {
            matchTcpUdpPorts = matchTcpUdpPortsEnabled;
            log.info("Configured. Matching TCP/UDP fields is {}", matchTcpUdpPorts ? "enabled" : "disabled");
        }
        boolean matchIcmpFieldsEnabled = isPropertyEnabled(properties, "matchIcmpFields");
        if (matchIcmpFields != matchIcmpFieldsEnabled) {
            matchIcmpFields = matchIcmpFieldsEnabled;
            log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
                    matchIcmpFields ? "enabled" : "disabled");
        }
        Integer flowTimeoutConfigured = getIntegerProperty(properties, "flowTimeout");
        if (flowTimeoutConfigured == null) {
            log.info("Flow Timeout is not configured, default value is {}", flowTimeout);
        } else {
            flowTimeout = flowTimeoutConfigured;
            log.info("Configured. Flow Timeout is configured to {}", flowTimeout, " seconds");
        }
        Integer flowPriorityConfigured = getIntegerProperty(properties, "flowPriority");
        if (flowPriorityConfigured == null) {
            log.info("Flow Priority is not configured, default value is {}", flowPriority);
        } else {
            flowPriority = flowPriorityConfigured;
            log.info("Configured. Flow Priority is configured to {}", flowPriority);
        }
    }

    /**
     * Get Integer property from the propertyName
     * Return null if propertyName is not found.
     *
     * @param properties   properties to be looked up
     * @param propertyName the name of the property to look up
     * @return value when the propertyName is defined or return null
     */
    private static Integer getIntegerProperty(Dictionary<?, ?> properties, String propertyName) {
        Integer value = null;
        try {
            String s = (String) properties.get(propertyName);
            value = isNullOrEmpty(s) ? value : Integer.parseInt(s.trim());
        } catch (NumberFormatException | ClassCastException e) {
            value = null;
        }
        return value;
    }

    /**
     * Check property name is defined and set to true.
     *
     * @param properties   properties to be looked up
     * @param propertyName the name of the property to look up
     * @return true when the propertyName is defined and set to true
     */
    private static boolean isPropertyEnabled(Dictionary<?, ?> properties, String propertyName) {
        boolean enabled = false;
        try {
            String flag = (String) properties.get(propertyName);
            if (flag != null) {
                enabled = flag.trim().equals("true");
            }
        } catch (ClassCastException e) {
            // No propertyName defined.
            enabled = false;
        }
        return enabled;
    }

    /**
     * Packet processor responsible for forwarding packets along their paths.
     */
    private class SDNTPacketProcessor implements PacketProcessor {

        @Override
        public void process(PacketContext context) {
            // Stop processing if the packet has been handled, since we
            // can't do any more to it.
            if (context.isHandled()) {
                return;
            }

            InboundPacket pkt = context.inPacket();
            Ethernet ethPkt = pkt.parsed();

            if (ethPkt == null) {
                return;
            }

            // Bail if this is deemed to be a control packet.
            if (isControlPacket(ethPkt)) {
                return;
            }

            HostId id = HostId.hostId(ethPkt.getDestinationMAC());
            HostId sid = HostId.hostId(ethPkt.getSourceMAC());

            // Do not process link-local addresses in any way.
            if (id.mac().isLinkLocal()) {
                return;
            }

            //log.info("smac: {}, dmac: {}, type: {}",
            //    ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(), ethPkt.getEtherType());
            //log.info("vlan: {}, loc: {}.", ethPkt.getVlanID(), context.inPacket().receivedFrom());

            // do proxy arp
            if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
                handleArp(context, ethPkt);
                return;
            }

            Boolean mapVlans = false;
            Short inVlan = -1;
            Short transVlan = -1;
            Short outVlan = -1;

            // lookup input, transit and output vlan IDs from mapping
            if (vlanTransMacMap.containsKey(ethPkt.getSourceMAC().toString())) {
                if (vlanTransMacMap.get(ethPkt.getSourceMAC().toString())
                        .containsKey(ethPkt.getDestinationMAC().toString())) {
                    if (vlanTransMacMap.get(ethPkt.getSourceMAC().toString())
                            .get(ethPkt.getDestinationMAC().toString()).containsKey(ethPkt.getVlanID())) {
                        inVlan = ethPkt.getVlanID();
                        transVlan = vlanTransMacMap.get(ethPkt.getSourceMAC().toString())
                                .get(ethPkt.getDestinationMAC().toString()).get(ethPkt.getVlanID());
                        if (vlanDstMacMap.containsKey(ethPkt.getSourceMAC().toString())) {
                            if (vlanDstMacMap.get(ethPkt.getSourceMAC().toString())
                                    .containsKey(ethPkt.getDestinationMAC().toString())) {
                                if (vlanDstMacMap.get(ethPkt.getSourceMAC().toString())
                                        .get(ethPkt.getDestinationMAC().toString()).containsKey(transVlan)) {
                                    outVlan = vlanDstMacMap.get(ethPkt.getSourceMAC().toString())
                                            .get(ethPkt.getDestinationMAC().toString()).get(transVlan);
                                    mapVlans = true;
                                }
                            }
                        }
                    }
                }
            }
            // if not remapping, use packet vlan
            if (!mapVlans) {
                inVlan = ethPkt.getVlanID();
                transVlan = ethPkt.getVlanID();
                outVlan = ethPkt.getVlanID();
            }

            // use vlan to get source and destination hosts
            sid = HostId.hostId(ethPkt.getSourceMAC(), VlanId.vlanId(inVlan));

            if (mapVlans) {
                id = HostId.hostId(ethPkt.getDestinationMAC(), VlanId.vlanId(outVlan));
            } else {
                id = HostId.hostId(ethPkt.getDestinationMAC(), VlanId.vlanId(inVlan));
            }

            //log.info("Hids: {}, Hidd: {}.", sid, id);

            Host dst = hostService.getHost(id);
            Host src = hostService.getHost(sid);

            //log.info("Hs: {}, Hd: {}.", src, dst);

            // Do we know who this is for? If not, arp must have failed.
            // Don't flood because the vlan could be wrong.
            if (dst == null) {
                return;
            }

            // see if flow is authorized, maybe do endpoint setup
            // ignore result until fully implemented
            flowAllowed();
            //if (!flowAllowed()) {
            //    return;
            //}

            // find the minimum port speed of the source and destination
            Port sPort = deviceService.getPort(src.location().deviceId(), src.location().port());
            Port dPort = deviceService.getPort(dst.location().deviceId(), dst.location().port());
            double targetSpeed = Math.min(sPort.portSpeed(), dPort.portSpeed());

            // get the set of paths that have a bottleneck bandwidth
            // of at least the minimum port speed
            Set<Path> paths = getPaths(src.location().deviceId(), dst.location().deviceId(), new BWWeight(),
                    targetSpeed);

            // if no paths exist, find the one with the max bottleneck bandwidth
            if (paths.isEmpty()) {
                paths = getPaths(src.location().deviceId(), dst.location().deviceId(), new BWWeight(), 0.0);
            }

            // if no paths exist, bail
            if (paths.isEmpty()) {
                log.info("No paths found.");
                return;
            }

            // choose path with max bottleneck bandwidth,
            // just an example selection, could be changed to something like hop
            // count or path delay
            Path path = getMaxBWPath(paths);

            // install flow rules and forward packet as needed
            installRules(context, path, mapVlans, inVlan, transVlan, outVlan);
        }

    }

    // do authentication
    // just a globus rest api example at the moment
    private boolean flowAllowed() {
        // get login info from file
        // TODO: get from somewhere else, or setup certs
        BufferedReader br = null;
        String uname;
        String password;
        try {
            br = new BufferedReader(new FileReader(GLOBUS_LOGIN_FILE));
            uname = br.readLine();
            password = br.readLine();
        } catch (IOException e) {
            log.info("Error reading globus login file: {}", GLOBUS_LOGIN_FILE);
            return false;
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException ex) {
                log.info("Error closing globus login file");
            }
        }

        if (uname == null || password == null) {
            log.info("Error: could not get globus login from file");
            return false;
        } else {
            log.info("Got globus login un: {}, pw: {}", uname, password);
        }

        // use login info to get authentication token
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(uname, password);
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpGet authGet = new HttpGet(GLOBUS_AUTH_URL);
        try {
            authGet.addHeader(new BasicScheme().authenticate(credentials, authGet));
        } catch (AuthenticationException e1) {
            e1.printStackTrace();
        }
        authGet.addHeader("User-Agent", "onos");
        log.info(authGet.toString());

        CloseableHttpResponse response1;
        try {
            response1 = httpclient.execute(authGet);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        String authTok = "";
        try {
            log.info(response1.toString());
            String resStr = EntityUtils.toString(response1.getEntity());
            log.info(resStr);
            JSONParser jsonParser = new JSONParser();
            JSONObject jsonObject = (JSONObject) jsonParser.parse(resStr);
            authTok = (String) jsonObject.get("access_token");
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } catch (ParseException e) {
            e.printStackTrace();
            return false;
        } catch (org.json.simple.parser.ParseException e) {
            e.printStackTrace();
        } finally {
            try {
                response1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        log.info("Token: {}", authTok);
        return true;
    }

    public Set<Path> getPaths(DeviceId src, DeviceId dst, EdgeWeight w, double bwThresh) {
        final DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
        final DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
        Topology topology = topologyService.currentTopology();
        if (!(topology instanceof DefaultTopology)) {
            log.info("Topology not DefaultTopology");
            return ImmutableSet.of();
        }
        DefaultTopology dtop = (DefaultTopology) topology;
        TopologyGraph graph = dtop.getGraph();
        Set<TopologyVertex> vertices = graph.getVertexes();
        if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
            // src or dst not part of the current graph
            return ImmutableSet.of();
        }

        // get all paths with the given bottleneck bandwidth threshold
        GraphPathSearch.Result<TopologyVertex, TopologyEdge> result = BWSEARCH.search(graph, srcV, dstV, w,
                ALL_PATHS, bwThresh);
        ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
        for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
            builder.add(networkPath(path));
        }
        return builder.build();
    }

    // Converts graph path to a network path with the same cost.
    private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
        List<Link> links = new ArrayList<>();
        for (TopologyEdge edge : path.edges()) {
            links.add(edge.link());
        }
        return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
    }

    // get path with max bottleneck bandwidth
    private Path getMaxBWPath(Set<Path> paths) {
        log.info("Got {} paths.", paths.size());
        Path retPath = null;
        double bestBW = -1.0;
        for (Path path : paths) {
            double pathLimit = Double.MAX_VALUE;
            for (Link link : path.links()) {
                for (ResourceRequest request : resourceService.getAvailableResources(link)) {
                    if (request.type() == ResourceType.BANDWIDTH) {
                        BandwidthResourceRequest brr = (BandwidthResourceRequest) request;
                        if (brr.bandwidth().toDouble() < pathLimit) {
                            pathLimit = brr.bandwidth().toDouble();
                        }
                    }
                }
            }
            log.info("Path limit: {}", pathLimit);

            if (pathLimit > bestBW) {
                bestBW = pathLimit;
                retPath = path;
            }
        }
        return retPath;
    }

    // class used to provide link bandwidth to path search algorithm
    private class BWWeight implements EdgeWeight {
        @Override
        public double weight(Edge edge) {
            if (!(edge instanceof TopologyEdge)) {
                log.info("Edge not TopologyEdge");
                return 1.0;
            }

            TopologyEdge tedge = (TopologyEdge) edge;

            for (ResourceRequest request : resourceService.getAvailableResources(tedge.link())) {
                if (request.type() == ResourceType.BANDWIDTH) {
                    BandwidthResourceRequest brr = (BandwidthResourceRequest) request;
                    log.info("Found edge bandwidth: {}, {}", brr.bandwidth().toDouble(), tedge);
                    return brr.bandwidth().toDouble();
                }
            }
            log.info("Unable to find edge BW");
            return 1.0;
        }
    }

    // Indicates whether this is a control packet, e.g. LLDP, BDDP
    private boolean isControlPacket(Ethernet eth) {
        short type = eth.getEtherType();
        return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
    }

    // Indicated whether this is an IPv6 multicast packet.
    private boolean isIpv6Multicast(Ethernet eth) {
        return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
    }

    // Selects a path from the given set that does not lead back to the
    // specified port.
    private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
        for (Path path : paths) {
            if (!path.src().port().equals(notToPort)) {
                return path;
            }
        }
        return null;
    }

    // Floods the specified packet if permissible.
    private void flood(PacketContext context) {
        if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
                context.inPacket().receivedFrom())) {
            packetOut(context, PortNumber.FLOOD);
        } else {
            context.block();
        }
    }

    // Sends a packet out the specified port.
    private void packetOut(PacketContext context, PortNumber portNumber) {
        context.treatmentBuilder().setOutput(portNumber);
        context.send();
    }

    private void installRules(PacketContext context, Path path, Boolean mapVlans, Short inVlan, Short transVlan,
            Short outVlan) {

        log.info("Installing rules.");

        InboundPacket pkt = context.inPacket();
        Ethernet ethPkt = pkt.parsed();
        HostId sid = HostId.hostId(ethPkt.getSourceMAC(), VlanId.vlanId(inVlan));
        HostId id;
        if (mapVlans) {
            id = HostId.hostId(ethPkt.getDestinationMAC(), VlanId.vlanId(outVlan));
        } else {
            id = HostId.hostId(ethPkt.getDestinationMAC(), VlanId.vlanId(inVlan));
        }
        Host dst = hostService.getHost(id);
        Host src = hostService.getHost(sid);

        // install rules in devices, moving from destination to source
        installRule(context, dst.location().port(), dst.location().deviceId(), mapVlans, transVlan, outVlan,
                2 * flowTimeout);
        for (ListIterator<Link> it = path.links().listIterator(path.links().size()); it.hasPrevious();) {
            Link link = it.previous();
            if (link.src().deviceId().equals(src.location().deviceId())) {
                installRule(context, link.src().port(), link.src().deviceId(), mapVlans, inVlan, transVlan,
                        flowTimeout);
            } else {
                installRule(context, link.src().port(), link.src().deviceId(), mapVlans, transVlan, transVlan,
                        2 * flowTimeout);
            }
        }

        // see if device with the packet is in the installed path
        if (deviceInPath(context.inPacket().receivedFrom().deviceId(), path)) {
            packetOut(context, PortNumber.TABLE);
        } else {
            // device is not in the path, sending to table would be very bad,
            // send to end of new path for now
            TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
            builder.setOutput(PortNumber.TABLE);
            packetService.emit(new DefaultOutboundPacket(dst.location().deviceId(), builder.build(),
                    ByteBuffer.wrap(ethPkt.serialize())));
        }
    }

    private boolean deviceInPath(DeviceId id, Path path) {
        for (Link link : path.links()) {
            if (link.src().deviceId().equals(id)) {
                return true;
            }
        }
        if (path.links().size() > 0) {
            if (path.links().get(path.links().size() - 1).dst().deviceId().equals(id)) {
                return true;
            }
        }
        return false;
    }

    // Install a rule forwarding the packet to the specified port.
    private void installRule(PacketContext context, PortNumber portNumber, DeviceId device, Boolean mapVlan,
            Short vlanIn, Short vlanOut, int timeout) {
        log.info("install rule: device: {}, port: {}, map: {}, vIn: {}, vout: {}", device, portNumber, mapVlan,
                vlanIn, vlanOut);

        Ethernet inPkt = context.inPacket().parsed();
        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();

        if (false) {
            selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
        } else {
            selectorBuilder.matchEthSrc(inPkt.getSourceMAC()).matchEthDst(inPkt.getDestinationMAC());

            // Match Vlan ID
            selectorBuilder.matchVlanId(VlanId.vlanId(vlanIn));

            //
            // If configured and EtherType is IPv4 - Match IPv4 and
            // TCP/UDP/ICMP fields
            //
            // not currently used
            if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
                IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
                byte ipv4Protocol = ipv4Packet.getProtocol();
                Ip4Prefix matchIp4SrcPrefix = Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
                        Ip4Prefix.MAX_MASK_LENGTH);
                Ip4Prefix matchIp4DstPrefix = Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
                        Ip4Prefix.MAX_MASK_LENGTH);
                selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPSrc(matchIp4SrcPrefix)
                        .matchIPDst(matchIp4DstPrefix);

                if (matchIpv4Dscp) {
                    byte dscp = ipv4Packet.getDscp();
                    byte ecn = ipv4Packet.getEcn();
                    selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
                }

                if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
                    TCP tcpPacket = (TCP) ipv4Packet.getPayload();
                    selectorBuilder.matchIPProtocol(ipv4Protocol).matchTcpSrc((short) tcpPacket.getSourcePort())
                            .matchTcpDst((short) tcpPacket.getDestinationPort());
                }
                if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
                    UDP udpPacket = (UDP) ipv4Packet.getPayload();
                    selectorBuilder.matchIPProtocol(ipv4Protocol).matchUdpSrc((short) udpPacket.getSourcePort())
                            .matchUdpDst((short) udpPacket.getDestinationPort());
                }
                if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
                    ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
                    selectorBuilder.matchIPProtocol(ipv4Protocol).matchIcmpType(icmpPacket.getIcmpType())
                            .matchIcmpCode(icmpPacket.getIcmpCode());
                }
            }

            //
            // If configured and EtherType is IPv6 - Match IPv6 and
            // TCP/UDP/ICMP fields
            //
            // not currently used
            if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
                IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
                byte ipv6NextHeader = ipv6Packet.getNextHeader();
                Ip6Prefix matchIp6SrcPrefix = Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
                        Ip6Prefix.MAX_MASK_LENGTH);
                Ip6Prefix matchIp6DstPrefix = Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
                        Ip6Prefix.MAX_MASK_LENGTH);
                selectorBuilder.matchEthType(Ethernet.TYPE_IPV6).matchIPv6Src(matchIp6SrcPrefix)
                        .matchIPv6Dst(matchIp6DstPrefix);

                if (matchIpv6FlowLabel) {
                    selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
                }

                if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
                    TCP tcpPacket = (TCP) ipv6Packet.getPayload();
                    selectorBuilder.matchIPProtocol(ipv6NextHeader).matchTcpSrc((short) tcpPacket.getSourcePort())
                            .matchTcpDst((short) tcpPacket.getDestinationPort());
                }
                if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
                    UDP udpPacket = (UDP) ipv6Packet.getPayload();
                    selectorBuilder.matchIPProtocol(ipv6NextHeader).matchUdpSrc((short) udpPacket.getSourcePort())
                            .matchUdpDst((short) udpPacket.getDestinationPort());
                }
                if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
                    ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
                    selectorBuilder.matchIPProtocol(ipv6NextHeader).matchIcmpv6Type(icmp6Packet.getIcmpType())
                            .matchIcmpv6Code(icmp6Packet.getIcmpCode());
                }
            }
        }

        // remap vlan if needed
        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
        if (mapVlan && !vlanIn.equals(vlanOut)) {
            treatment = treatment.setVlanId(VlanId.vlanId(vlanOut));
        }
        // set output port on device
        treatment = treatment.setOutput(portNumber);

        ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
                .withSelector(selectorBuilder.build()).withTreatment(treatment.build()).withPriority(flowPriority)
                .withFlag(ForwardingObjective.Flag.VERSATILE).fromApp(appId).makeTemporary(timeout).add();

        flowObjectiveService.forward(device, forwardingObjective);
    }

    private boolean handleArp(PacketContext context, Ethernet ethPkt) {
        ARP arp = (ARP) ethPkt.getPayload();
        //log.info("Doing arp.");

        // determine the destination vlan from map and set to arp packet
        short dstVlan = ethPkt.getVlanID();
        if (vlanDstIpMap.containsKey(ethPkt.getSourceMAC().toString())) {
            Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress());
            if (vlanDstIpMap.get(ethPkt.getSourceMAC().toString()).containsKey(targetAddress.toString())) {
                if (vlanDstIpMap.get(ethPkt.getSourceMAC().toString()).get(targetAddress.toString())
                        .containsKey(dstVlan)) {
                    dstVlan = vlanDstIpMap.get(ethPkt.getSourceMAC().toString()).get(targetAddress.toString())
                            .get(dstVlan);
                }
            }
        }
        ethPkt.setVlanID(dstVlan);

        // process arp
        if (arp.getOpCode() == ARP.OP_REPLY) {
            //log.info("Arp reply.");
            forward(ethPkt, context.inPacket().receivedFrom());
        } else if (arp.getOpCode() == ARP.OP_REQUEST) {
            //log.info("Arp request.");
            replyArp(ethPkt, context.inPacket().receivedFrom());
        } else {
            //log.info("Unknown Arp op.");
            return false;
        }
        context.block();
        return true;
    }

    private void forward(Ethernet eth, ConnectPoint inPort) {
        //checkPermission(Permission.PACKET_WRITE);

        checkNotNull(eth, REQUEST_NULL);

        Host h = hostService.getHost(HostId.hostId(eth.getDestinationMAC(), VlanId.vlanId(eth.getVlanID())));

        if (h == null) {
            flood(eth, inPort);
        } else {
            TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
            builder.setOutput(h.location().port());
            packetService.emit(new DefaultOutboundPacket(h.location().deviceId(), builder.build(),
                    ByteBuffer.wrap(eth.serialize())));
        }

    }

    private void replyArp(Ethernet eth, ConnectPoint inPort) {
        ARP arp = (ARP) eth.getPayload();
        checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
        checkNotNull(inPort);
        Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress());

        VlanId vlan = VlanId.vlanId(eth.getVlanID());

        if (isOutsidePort(inPort)) {
            // If the request came from outside the network, only reply if it was
            // for one of our external addresses.
            Set<PortAddresses> addressSet = hostService.getAddressBindingsForPort(inPort);

            for (PortAddresses addresses : addressSet) {
                for (InterfaceIpAddress ia : addresses.ipAddresses()) {
                    if (ia.ipAddress().equals(targetAddress)) {
                        Ethernet arpReply = ARP.buildArpReply(targetAddress, addresses.mac(), eth);
                        sendTo(arpReply, inPort);
                    }
                }
            }
            return;
        }

        // See if we have the target host in the host store

        Set<Host> hosts = hostService.getHostsByIp(targetAddress);

        Host dst = null;
        Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(), VlanId.vlanId(eth.getVlanID())));

        for (Host host : hosts) {
            if (host.vlan().equals(vlan)) {
                dst = host;
                break;
            }
        }

        if (src != null && dst != null) {
            //log.info("Arp dst known.");
            // We know the target host so we can respond
            Ethernet arpReply = ARP.buildArpReply(targetAddress, dst.mac(), eth);
            sendTo(arpReply, inPort);
            return;
        }
        //log.info("Arp dst not known.");

        // If the source address matches one of our external addresses
        // it could be a request from an internal host to an external
        // address. Forward it over to the correct port.
        Ip4Address source = Ip4Address.valueOf(arp.getSenderProtocolAddress());
        Set<PortAddresses> sourceAddresses = findPortsInSubnet(source);
        boolean matched = false;
        for (PortAddresses pa : sourceAddresses) {
            for (InterfaceIpAddress ia : pa.ipAddresses()) {
                if (ia.ipAddress().equals(source) && pa.vlan().equals(vlan)) {
                    matched = true;
                    sendTo(eth, pa.connectPoint());
                    break;
                }
            }
        }

        if (matched) {
            return;
        }

        //
        // The request couldn't be resolved.
        // Flood the request on all ports except the incoming port.
        //
        flood(eth, inPort);
        return;
    }

    private boolean isOutsidePort(ConnectPoint port) {
        //
        // TODO: Is this sufficient to identify outside-facing ports: just
        // having IP addresses on a port?
        //
        return !hostService.getAddressBindingsForPort(port).isEmpty();
    }

    private void sendTo(Ethernet packet, ConnectPoint outPort) {
        if (!edgeService.isEdgePoint(outPort)) {
            // Sanity check to make sure we don't send the packet out an
            // internal port and create a loop (could happen due to
            // misconfiguration).
            return;
        }

        TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
        builder.setOutput(outPort.port());
        packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), builder.build(),
                ByteBuffer.wrap(packet.serialize())));
    }

    private Set<PortAddresses> findPortsInSubnet(IpAddress target) {
        Set<PortAddresses> result = new HashSet<>();
        for (PortAddresses addresses : hostService.getAddressBindings()) {
            result.addAll(addresses.ipAddresses().stream().filter(ia -> ia.subnetAddress().contains(target))
                    .map(ia -> addresses).collect(Collectors.toList()));
        }
        return result;
    }

    private void flood(Ethernet request, ConnectPoint inPort) {
        //log.info("Flood edge ports, vlan: {}.", request.getVlanID());
        TrafficTreatment.Builder builder = null;
        ByteBuffer buf = ByteBuffer.wrap(request.serialize());

        for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
            if (isOutsidePort(connectPoint) || connectPoint.equals(inPort)) {
                continue;
            }

            //log.info("Arp Send connect point: {}.", connectPoint);
            builder = DefaultTrafficTreatment.builder();
            builder.setOutput(connectPoint.port());
            packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(), builder.build(), buf));
        }

    }
}