Package net.floodlightcontroller.virtualnetwork

Source Code of net.floodlightcontroller.virtualnetwork.VirtualNetworkFilter

/**
*    Copyright 2013, Big Switch Networks, Inc.
*
*    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 net.floodlightcontroller.virtualnetwork;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFType;
import org.openflow.protocol.action.OFAction;
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.util.AppCookie;
import net.floodlightcontroller.devicemanager.IDevice;
import net.floodlightcontroller.devicemanager.IDeviceListener;
import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.packet.DHCP;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPacket;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.routing.ForwardingBase;
import net.floodlightcontroller.util.MACAddress;

/**
* A simple Layer 2 (MAC based) network virtualization module. This module allows
* you to create simple L2 networks (host + gateway) and will drop traffic if
* they are not on the same virtual network.
*
* LIMITATIONS
* - This module does not allow overlapping of IPs or MACs
* - You can only have 1 gateway per virtual network (can be shared)
* - There is filtering of multicast/broadcast traffic
* - All DHCP traffic will be allowed, regardless of unicast/broadcast
*
* @author alexreimers
*/
public class VirtualNetworkFilter
    implements IFloodlightModule, IVirtualNetworkService, IOFMessageListener {
    protected static Logger log = LoggerFactory.getLogger(VirtualNetworkFilter.class);

    private static final short APP_ID = 20;
    static {
        AppCookie.registerApp(APP_ID, "VirtualNetworkFilter");
    }

    // Our dependencies
    IFloodlightProviderService floodlightProvider;
    IRestApiService restApi;
    IDeviceService deviceService;

    // Our internal state
    protected Map<String, VirtualNetwork> vNetsByGuid; // List of all created virtual networks
    protected Map<String, String> nameToGuid; // Logical name -> Network ID
    protected Map<String, Integer> guidToGateway; // Network ID -> Gateway IP
    protected Map<Integer, Set<String>> gatewayToGuid; // Gateway IP -> Network ID
    protected Map<MACAddress, Integer> macToGateway; // Gateway MAC -> Gateway IP
    protected Map<MACAddress, String> macToGuid; // Host MAC -> Network ID
    protected Map<String, MACAddress> portToMac; // Host MAC -> logical port name

    // Device Listener impl class
    protected DeviceListenerImpl deviceListener;

    /**
     * Adds a gateway to a virtual network.
     * @param guid The ID (not name) of the network.
     * @param ip The IP addresses of the gateway.
     */
    protected void addGateway(String guid, Integer ip) {
        if (ip.intValue() != 0) {
            if (log.isDebugEnabled()) {
                log.debug("Adding {} as gateway for GUID {}",
                    IPv4.fromIPv4Address(ip), guid);
            }

            guidToGateway.put(guid, ip);
            if (vNetsByGuid.get(guid) != null)
                vNetsByGuid.get(guid).setGateway(IPv4.fromIPv4Address(ip));
            if (gatewayToGuid.containsKey(ip)) {
                Set<String> gSet = gatewayToGuid.get(ip);
                gSet.add(guid);
            } else {
                Set<String> gSet = Collections.synchronizedSet(new HashSet<String>());
                gSet.add(guid);
                gatewayToGuid.put(ip, gSet);
            }
        }
    }

    /**
     * Deletes a gateway for a virtual network.
     * @param guid The ID (not name) of the network to delete
     * the gateway for.
     */
    protected void deleteGateway(String guid) {
        Integer gwIp = guidToGateway.remove(guid);
        if (gwIp == null) return;
        Set<String> gSet = gatewayToGuid.get(gwIp);
        gSet.remove(guid);
        if(vNetsByGuid.get(guid)!=null)
            vNetsByGuid.get(guid).setGateway(null);
    }

    // IVirtualNetworkService

    @Override
    public void createNetwork(String guid, String network, Integer gateway) {
        if (log.isDebugEnabled()) {
            String gw = null;
            try {
                gw = IPv4.fromIPv4Address(gateway);
            } catch (Exception e) {
                // fail silently
            }
            log.debug("Creating network {} with ID {} and gateway {}",
                      new Object[] {network, guid, gw});
        }

        if (!nameToGuid.isEmpty()) {
            // We have to iterate all the networks to handle name/gateway changes
            for (Entry<String, String> entry : nameToGuid.entrySet()) {
                if (entry.getValue().equals(guid)) {
                    nameToGuid.remove(entry.getKey());
                    break;
                }
            }
        }
        if(network != null)
            nameToGuid.put(network, guid);
        if (vNetsByGuid.containsKey(guid))
            vNetsByGuid.get(guid).setName(network); //network already exists, just updating name
        else
            vNetsByGuid.put(guid, new VirtualNetwork(network, guid)); //new network

        // If they don't specify a new gateway the old one will be preserved
        if ((gateway != null) && (gateway != 0)) {
            addGateway(guid, gateway);
            if(vNetsByGuid.get(guid)!=null)
                vNetsByGuid.get(guid).setGateway(IPv4.fromIPv4Address(gateway));
        }
    }

    @Override
    public void deleteNetwork(String guid) {
        String name = null;
        if (nameToGuid.isEmpty()) {
            log.warn("Could not delete network with ID {}, network doesn't exist",
                     guid);
            return;
        }
        for (Entry<String, String> entry : nameToGuid.entrySet()) {
            if (entry.getValue().equals(guid)) {
                name = entry.getKey();
                break;
            }
            log.warn("Could not delete network with ID {}, network doesn't exist",
                     guid);
        }

        if (log.isDebugEnabled())
            log.debug("Deleting network with name {} ID {}", name, guid);

        nameToGuid.remove(name);
        deleteGateway(guid);
        if(vNetsByGuid.get(guid)!=null){
            vNetsByGuid.get(guid).clearHosts();
            vNetsByGuid.remove(guid);
        }
        Collection<MACAddress> deleteList = new ArrayList<MACAddress>();
        for (MACAddress host : macToGuid.keySet()) {
            if (macToGuid.get(host).equals(guid)) {
                deleteList.add(host);
            }
        }
        for (MACAddress mac : deleteList) {
            if (log.isDebugEnabled()) {
                log.debug("Removing host {} from network {}",
                          HexString.toHexString(mac.toBytes()), guid);
            }
            macToGuid.remove(mac);
            for (Entry<String, MACAddress> entry : portToMac.entrySet()) {
                if (entry.getValue().equals(mac)) {
                    portToMac.remove(entry.getKey());
                    break;
                }
            }
        }
    }

    @Override
    public void addHost(MACAddress mac, String guid, String port) {
        if (guid != null) {
            if (log.isDebugEnabled()) {
                log.debug("Adding {} to network ID {} on port {}",
                          new Object[] {mac, guid, port});
            }
            // We ignore old mappings
            macToGuid.put(mac, guid);
            portToMac.put(port, mac);
            if(vNetsByGuid.get(guid)!=null)
                vNetsByGuid.get(guid).addHost(port,new MACAddress(mac.toBytes()));
        } else {
            log.warn("Could not add MAC {} to network ID {} on port {}, the network does not exist",
                     new Object[] {mac, guid, port});
        }
    }

    @Override
    public void deleteHost(MACAddress mac, String port) {
        if (log.isDebugEnabled()) {
            log.debug("Removing host {} from port {}", mac, port);
        }
        if (mac == null && port == null) return;
        if (port != null) {
            MACAddress host = portToMac.remove(port);
            if(host !=null && vNetsByGuid.get(macToGuid.get(host)) != null)
                vNetsByGuid.get(macToGuid.get(host)).removeHost(host);
      if(host !=null)
              macToGuid.remove(host);
        } else if (mac != null) {
            if (!portToMac.isEmpty()) {
                for (Entry<String, MACAddress> entry : portToMac.entrySet()) {
                    if (entry.getValue().equals(mac)) {
                        if(vNetsByGuid.get(macToGuid.get(entry.getValue())) != null)
                            vNetsByGuid.get(macToGuid.get(entry.getValue())).removeHost(entry.getValue());
                        portToMac.remove(entry.getKey());
                        macToGuid.remove(entry.getValue());
                        return;
                    }
                }
            }
        }
    }

    // IFloodlightModule

    @Override
    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
        Collection<Class<? extends IFloodlightService>> l =
                new ArrayList<Class<? extends IFloodlightService>>();
        l.add(IVirtualNetworkService.class);
        return l;
    }

    @Override
    public Map<Class<? extends IFloodlightService>, IFloodlightService>
            getServiceImpls() {
        Map<Class<? extends IFloodlightService>,
            IFloodlightService> m =
                new HashMap<Class<? extends IFloodlightService>,
                    IFloodlightService>();
        m.put(IVirtualNetworkService.class, this);
        return m;
    }

    @Override
    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
        Collection<Class<? extends IFloodlightService>> l =
                new ArrayList<Class<? extends IFloodlightService>>();
        l.add(IFloodlightProviderService.class);
        l.add(IRestApiService.class);
        l.add(IDeviceService.class);
        return l;
    }

    @Override
    public void init(FloodlightModuleContext context)
                                 throws FloodlightModuleException {
        floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
        restApi = context.getServiceImpl(IRestApiService.class);
        deviceService = context.getServiceImpl(IDeviceService.class);

        vNetsByGuid = new ConcurrentHashMap<String, VirtualNetwork>();
        nameToGuid = new ConcurrentHashMap<String, String>();
        guidToGateway = new ConcurrentHashMap<String, Integer>();
        gatewayToGuid = new ConcurrentHashMap<Integer, Set<String>>();
        macToGuid = new ConcurrentHashMap<MACAddress, String>();
        portToMac = new ConcurrentHashMap<String, MACAddress>();
        macToGateway = new ConcurrentHashMap<MACAddress, Integer>();
        deviceListener = new DeviceListenerImpl();

    }

    @Override
    public void startUp(FloodlightModuleContext context) {
        floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
        restApi.addRestletRoutable(new VirtualNetworkWebRoutable());
        deviceService.addListener(this.deviceListener);
    }

    // IOFMessageListener

    @Override
    public String getName() {
        return "virtualizer";
    }

    @Override
    public boolean isCallbackOrderingPrereq(OFType type, String name) {
        // Link discovery should go before us so we don't block LLDPs
        return (type.equals(OFType.PACKET_IN) &&
                (name.equals("linkdiscovery") || (name.equals("devicemanager"))));
    }

    @Override
    public boolean isCallbackOrderingPostreq(OFType type, String name) {
        // We need to go before forwarding
        return (type.equals(OFType.PACKET_IN) && name.equals("forwarding"));
    }

    @Override
    public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
        switch (msg.getType()) {
            case PACKET_IN:
                return processPacketIn(sw, (OFPacketIn)msg, cntx);
            default:
                break;
        }
        log.warn("Received unexpected message {}", msg);
        return Command.CONTINUE;
    }

    /**
     * Checks whether the frame is destined to or from a gateway.
     * @param frame The ethernet frame to check.
     * @return True if it is to/from a gateway, false otherwise.
     */
    protected boolean isDefaultGateway(Ethernet frame) {
        if (macToGateway.containsKey(frame.getSourceMAC()))
            return true;

        Integer gwIp = macToGateway.get(frame.getDestinationMAC());
        if (gwIp != null) {
            MACAddress host = frame.getSourceMAC();
            String srcNet = macToGuid.get(host);
            if (srcNet != null) {
                Integer gwIpSrcNet = guidToGateway.get(srcNet);
                if ((gwIpSrcNet != null) && (gwIp.equals(gwIpSrcNet)))
                    return true;
            }
        }

        return false;
    }

    /**
     * Checks to see if two MAC Addresses are on the same network.
     * @param m1 The first MAC.
     * @param m2 The second MAC.
     * @return True if they are on the same virtual network,
     *          false otherwise.
     */
    protected boolean oneSameNetwork(MACAddress m1, MACAddress m2) {
        String net1 = macToGuid.get(m1);
        String net2 = macToGuid.get(m2);
        if (net1 == null) return false;
        if (net2 == null) return false;
        return net1.equals(net2);
    }

    /**
     * Checks to see if an Ethernet frame is a DHCP packet.
     * @param frame The Ethernet frame.
     * @return True if it is a DHCP frame, false otherwise.
     */
    protected boolean isDhcpPacket(Ethernet frame) {
        IPacket payload = frame.getPayload(); // IP
        if (payload == null) return false;
        IPacket p2 = payload.getPayload(); // TCP or UDP
        if (p2 == null) return false;
        IPacket p3 = p2.getPayload(); // Application
        if ((p3 != null) && (p3 instanceof DHCP)) return true;
        return false;
    }

    /**
     * Processes an OFPacketIn message and decides if the OFPacketIn should be dropped
     * or the processing should continue.
     * @param sw The switch the PacketIn came from.
     * @param msg The OFPacketIn message from the switch.
     * @param cntx The FloodlightContext for this message.
     * @return Command.CONTINUE if processing should be continued, Command.STOP otherwise.
     */
    protected Command processPacketIn(IOFSwitch sw, OFPacketIn msg, FloodlightContext cntx) {
        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
                                              IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
        Command ret = Command.STOP;
        String srcNetwork = macToGuid.get(eth.getSourceMAC());
        // If the host is on an unknown network we deny it.
        // We make exceptions for ARP and DHCP.
        if (eth.isBroadcast() || eth.isMulticast() || isDefaultGateway(eth) || isDhcpPacket(eth)) {
            ret = Command.CONTINUE;
        } else if (srcNetwork == null) {
            log.trace("Blocking traffic from host {} because it is not attached to any network.",
                      HexString.toHexString(eth.getSourceMACAddress()));
            ret = Command.STOP;
        } else if (oneSameNetwork(eth.getSourceMAC(), eth.getDestinationMAC())) {
            // if they are on the same network continue
            ret = Command.CONTINUE;
        }

        if (log.isTraceEnabled())
            log.trace("Results for flow between {} and {} is {}",
                    new Object[] {eth.getSourceMAC(), eth.getDestinationMAC(), ret});
        /*
         * TODO - figure out how to still detect gateways while using
         * drop mods
        if (ret == Command.STOP) {
            if (!(eth.getPayload() instanceof ARP))
                doDropFlow(sw, msg, cntx);
        }
        */
        return ret;
    }

    /**
     * Writes a FlowMod to a switch that inserts a drop flow.
     * @param sw The switch to write the FlowMod to.
     * @param pi The corresponding OFPacketIn. Used to create the OFMatch structure.
     * @param cntx The FloodlightContext that gets passed to the switch.
     */
    protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
        if (log.isTraceEnabled()) {
            log.trace("doDropFlow pi={} srcSwitch={}",
                    new Object[] { pi, sw });
        }

        if (sw == null) {
            log.warn("Switch is null, not installing drop flowmod for PacketIn {}", pi);
            return;
        }

        // Create flow-mod based on packet-in and src-switch
        OFFlowMod fm =
            (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
        OFMatch match = new OFMatch();
        match.loadFromPacket(pi.getPacketData(), pi.getInPort());
        List<OFAction> actions = new ArrayList<OFAction>(); // no actions = drop
        long cookie = AppCookie.makeCookie(APP_ID, 0);
        fm.setCookie(cookie)
        .setIdleTimeout(ForwardingBase.FLOWMOD_DEFAULT_IDLE_TIMEOUT)
        .setHardTimeout(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT)
        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
        .setMatch(match)
        .setActions(actions)
        .setLengthU(OFFlowMod.MINIMUM_LENGTH);
//        fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
        try {
            if (log.isTraceEnabled()) {
                log.trace("write drop flow-mod srcSwitch={} match={} " +
                          "pi={} flow-mod={}",
                          new Object[] {sw, match, pi, fm});
            }
            sw.write(fm, cntx);
        } catch (IOException e) {
            log.error("Failure writing drop flow mod", e);
        }
        return;
    }

    @Override
    public Collection <VirtualNetwork> listNetworks() {
        return vNetsByGuid.values();
    }

    // IDeviceListener
    class DeviceListenerImpl implements IDeviceListener{
        @Override
        public void deviceAdded(IDevice device) {
            if (device.getIPv4Addresses() == null) return;
            for (Integer i : device.getIPv4Addresses()) {
                if (gatewayToGuid.containsKey(i)) {
                    MACAddress mac = MACAddress.valueOf(device.getMACAddress());
                    if (log.isDebugEnabled())
                        log.debug("Adding MAC {} with IP {} a a gateway",
                                    HexString.toHexString(mac.toBytes()),
                                    IPv4.fromIPv4Address(i));
                        macToGateway.put(mac, i);
                    }
                }
            }

            @Override
        public void deviceRemoved(IDevice device) {
            // if device is a gateway remove
            MACAddress mac = MACAddress.valueOf(device.getMACAddress());
            if (macToGateway.containsKey(mac)) {
                if (log.isDebugEnabled())
                    log.debug("Removing MAC {} as a gateway",
                                HexString.toHexString(mac.toBytes()));
                macToGateway.remove(mac);
             }
        }

        @Override
        public void deviceIPV4AddrChanged(IDevice device) {
            // add or remove entry as gateway
            deviceAdded(device);
        }

        @Override
        public void deviceMoved(IDevice device) {
            // ignore
        }

        @Override
        public void deviceVlanChanged(IDevice device) {
            // ignore
        }

        @Override
        public String getName() {
            return VirtualNetworkFilter.this.getName();
        }

        @Override
        public boolean isCallbackOrderingPrereq(String type, String name) {
            return false;
        }

        @Override
        public boolean isCallbackOrderingPostreq(String type, String name) {
            // We need to go before forwarding
            return false;
        }
    }
}
TOP

Related Classes of net.floodlightcontroller.virtualnetwork.VirtualNetworkFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.