2 * Copyright 2015 Open Networking Laboratory
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.onosproject.openstackswitching;
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Maps;
20 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.apache.felix.scr.annotations.Service;
26 import org.onlab.packet.Ethernet;
27 import org.onlab.packet.IPv4;
28 import org.onlab.packet.Ip4Address;
29 import org.onlab.packet.Ip4Prefix;
30 import org.onlab.packet.MacAddress;
31 import org.onlab.packet.UDP;
32 import org.onosproject.core.ApplicationId;
33 import org.onosproject.core.CoreService;
34 import org.onosproject.net.Device;
35 import org.onosproject.net.DeviceId;
36 import org.onosproject.net.Port;
37 import org.onosproject.net.device.DeviceEvent;
38 import org.onosproject.net.device.DeviceListener;
39 import org.onosproject.net.device.DeviceService;
40 import org.onosproject.net.flowobjective.FlowObjectiveService;
41 import org.onosproject.net.packet.InboundPacket;
42 import org.onosproject.net.packet.PacketContext;
43 import org.onosproject.net.packet.PacketProcessor;
44 import org.onosproject.net.packet.PacketService;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.concurrent.ExecutorService;
51 import java.util.concurrent.Executors;
53 @SuppressWarnings("ALL")
55 @Component(immediate = true)
57 * It populates forwarding rules for VMs created by Openstack.
59 public class OpenstackSwitchingManager implements OpenstackSwitchingService {
61 private static Logger log = LoggerFactory
62 .getLogger(OpenstackSwitchingManager.class);
64 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 protected CoreService coreService;
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected PacketService packetService;
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected DeviceService deviceService;
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected FlowObjectiveService flowObjectiveService;
77 public static final int DHCP_PORT = 67;
79 private ApplicationId appId;
80 private OpenstackArpHandler arpHandler;
81 private OpenstackDhcpHandler dhcpHandler = new OpenstackDhcpHandler();
82 private OpenstackSwitchingRulePopulator rulePopulator;
83 private ExecutorService deviceEventExcutorService = Executors.newFixedThreadPool(10);
85 private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
86 private InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
88 // Map <port_id, OpenstackPort>
89 private HashMap<String, OpenstackPort> openstackPortMap;
90 // Map <network_id, OpenstackNetwork>
91 private HashMap<String, OpenstackNetwork> openstackNetworkMap;
92 // Map <vni, List <Entry <portName, host ip>>
93 private HashMap<String, List<PortInfo>> vniPortMap;
94 private HashMap<Ip4Address, Port> tunnelPortMap;
98 protected void activate() {
100 .registerApplication("org.onosproject.openstackswitching");
101 rulePopulator = new OpenstackSwitchingRulePopulator(appId, flowObjectiveService);
102 packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
103 deviceService.addListener(internalDeviceListener);
105 openstackPortMap = Maps.newHashMap();
106 openstackNetworkMap = Maps.newHashMap();
107 vniPortMap = Maps.newHashMap();
108 tunnelPortMap = Maps.newHashMap();
110 arpHandler = new OpenstackArpHandler(openstackPortMap);
116 protected void deactivate() {
117 packetService.removeProcessor(internalPacketProcessor);
118 deviceService.removeListener(internalDeviceListener);
120 deviceEventExcutorService.shutdown();
126 public void createPorts(OpenstackPort openstackPort) {
127 openstackPortMap.put(openstackPort.id(), openstackPort);
131 public void deletePorts() {
136 public void updatePorts() {
141 public void createNetwork(OpenstackNetwork openstackNetwork) {
142 openstackNetworkMap.put(openstackNetwork.id(), openstackNetwork);
145 private void processDeviceAdded(Device device) {
146 log.warn("device {} is added", device.id());
147 rulePopulator.populateDefaultRules(device.id());
150 private void processPortAdded(Device device, Port port) {
151 // TODO: Simplify the data structure to store the network info
152 // TODO: Make it stateless
153 // TODO: All the logics need to be processed inside of the rulePopulator class
154 synchronized (vniPortMap) {
155 log.warn("port {} is updated", port.toString());
157 updatePortMaps(device, port);
158 if (!port.annotations().value("portName").equals("vxlan")) {
159 populateFlowRulesForTrafficToSameCnode(device, port);
160 populateFlowRulesForTrafficToDifferentCnode(device, port);
165 private void processPortRemoved(Device device, Port port) {
166 log.warn("port {} is removed", port.toString());
167 // TODO: need to update the vniPortMap
171 * Populates the flow rules for traffic to VMs in different Cnode using
174 * @param device device to put rules
175 * @param port port information of the VM
177 private void populateFlowRulesForTrafficToDifferentCnode(Device device, Port port) {
178 String portName = port.annotations().value("portName");
179 String channelId = device.annotations().value("channelId");
180 Ip4Address hostIpAddress = Ip4Address.valueOf(channelId.split(":")[0]);
181 Ip4Address fixedIp = getFixedIpAddressForPort(portName);
182 // TODO: Avoid duplicate flow rule set up for VMs in other Cnode
183 // (possibly avoided by flowrule subsystem?)
184 if (tunnelPortMap.get(hostIpAddress) == null) {
185 log.warn("There is no tunnel port information");
188 String vni = getVniForPort(portName);
189 MacAddress vmMac = getVmMacAddressForPort(portName);
190 if (!vniPortMap.isEmpty() && vniPortMap.get(vni) != null) {
191 for (PortInfo portInfo : vniPortMap.get(vni)) {
192 if (!portInfo.portName.equals(portName) &&
193 !portInfo.hostIp.equals(hostIpAddress)) {
194 MacAddress vmMacx = getVmMacAddressForPort(portInfo.portName);
195 rulePopulator.populateForwardingRuleForOtherCnode(vni,
196 device.id(), portInfo.hostIp, portInfo.fixedIp, vmMacx,
197 tunnelPortMap.get(hostIpAddress).number(),
198 portInfo.deviceId, hostIpAddress, fixedIp, vmMac,
199 tunnelPortMap.get(portInfo.hostIp).number());
206 * Populates the flow rules for traffic to VMs in the same Cnode as the sender.
208 * @param device device to put the rules
209 * @param port port info of the VM
211 private void populateFlowRulesForTrafficToSameCnode(Device device, Port port) {
212 Ip4Prefix cidr = getCidrForPort(port.annotations().value("portName"));
213 Ip4Address vmIp = getFixedIpAddressForPort(port.annotations().value("portName"));
215 rulePopulator.populateForwardingRule(vmIp, device.id(), port, cidr);
220 * Updates the port maps using the port information.
222 * @param device device info
223 * @param port port of the VM
225 private void updatePortMaps(Device device, Port port) {
226 String portName = port.annotations().value("portName");
227 String channelId = device.annotations().value("channelId");
228 Ip4Address hostIpAddress = Ip4Address.valueOf(channelId.split(":")[0]);
229 if (portName.startsWith("vxlan")) {
230 tunnelPortMap.put(hostIpAddress, port);
232 String vni = getVniForPort(portName);
233 Ip4Address fixedIp = getFixedIpAddressForPort(portName);
234 if (vniPortMap.get(vni) == null) {
235 vniPortMap.put(vni, Lists.newArrayList());
237 vniPortMap.get(vni).add(new PortInfo(device.id(), portName, fixedIp, hostIpAddress));
242 * Returns CIDR information from the subnet map for the port.
244 * @param portName port name of the port of the VM
245 * @return CIDR of the VNI of the VM
247 private Ip4Prefix getCidrForPort(String portName) {
248 String networkId = null;
249 String uuid = portName.substring(3);
250 OpenstackPort port = openstackPortMap.values().stream()
251 .filter(p -> p.id().startsWith(uuid))
254 log.warn("No port information for port {}", portName);
258 //OpenstackSubnet subnet = openstackSubnetMap.values().stream()
259 // .filter(s -> s.networkId().equals(port.networkId()))
260 // .findFirst().get();
261 //if (subnet == null) {
262 // log.warn("No subnet information for network {}", subnet.id());
266 //return Ip4Prefix.valueOf(subnet.cidr());
271 * Returns the VNI of the VM of the port.
273 * @param portName VM port
276 private String getVniForPort(String portName) {
277 String networkId = null;
278 String uuid = portName.substring(3);
279 OpenstackPort port = openstackPortMap.values().stream()
280 .filter(p -> p.id().startsWith(uuid))
283 log.warn("No port information for port {}", portName);
286 OpenstackNetwork network = openstackNetworkMap.values().stream()
287 .filter(n -> n.id().equals(port.networkId()))
289 if (network == null) {
290 log.warn("No VNI information for network {}", network.id());
294 return network.segmentId();
298 * Returns the Fixed IP address of the VM.
300 * @param portName VM port info
301 * @return IP address of the VM
303 private Ip4Address getFixedIpAddressForPort(String portName) {
305 // FIXME - For now we use the information stored from neutron Rest API call.
306 // TODO - Later, the information needs to be extracted from Neutron on-demand.
307 String uuid = portName.substring(3);
308 OpenstackPort port = openstackPortMap.values().stream()
309 .filter(p -> p.id().startsWith(uuid))
313 log.error("There is no port information for port name {}", portName);
317 if (port.fixedIps().isEmpty()) {
318 log.error("There is no fixed IP info in the port information");
322 return (Ip4Address) port.fixedIps().values().toArray()[0];
326 * Returns the MAC address of the VM of the port.
328 * @param portName VM port
329 * @return MAC address of the VM
331 private MacAddress getVmMacAddressForPort(String portName) {
333 String uuid = portName.substring(3);
334 OpenstackPort port = openstackPortMap.values().stream()
335 .filter(p -> p.id().startsWith(uuid))
339 log.error("There is no mac information for port name {}", portName);
343 return port.macAddress();
346 private class InternalPacketProcessor implements PacketProcessor {
349 public void process(PacketContext context) {
351 if (context.isHandled()) {
355 InboundPacket pkt = context.inPacket();
356 Ethernet ethernet = pkt.parsed();
358 if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
359 arpHandler.processPacketIn(pkt);
360 } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
361 IPv4 ipPacket = (IPv4) ethernet.getPayload();
363 if (ipPacket.getProtocol() == IPv4.PROTOCOL_UDP) {
364 UDP udpPacket = (UDP) ipPacket.getPayload();
365 if (udpPacket.getDestinationPort() == DHCP_PORT) {
366 dhcpHandler.processPacketIn(pkt);
373 private class InternalDeviceListener implements DeviceListener {
376 public void event(DeviceEvent event) {
377 deviceEventExcutorService.execute(new InternalEventHandler(event));
381 private class InternalEventHandler implements Runnable {
383 volatile DeviceEvent deviceEvent;
385 InternalEventHandler(DeviceEvent deviceEvent) {
386 this.deviceEvent = deviceEvent;
391 switch (deviceEvent.type()) {
393 processDeviceAdded((Device) deviceEvent.subject());
396 Port port = (Port) deviceEvent.subject();
397 if (port.isEnabled()) {
398 processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
401 case DEVICE_AVAILABILITY_CHANGED:
402 Device device = (Device) deviceEvent.subject();
403 if (deviceService.isAvailable(device.id())) {
404 processDeviceAdded(device);
408 processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
411 processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
414 processPortRemoved((Device) deviceEvent.subject(), deviceEvent.port());
422 private final class PortInfo {
428 private PortInfo(DeviceId deviceId, String portName, Ip4Address fixedIp,
430 this.deviceId = deviceId;
431 this.portName = portName;
432 this.fixedIp = fixedIp;
433 this.hostIp = hostIp;