Daniel Park
Committed by Gerrit Code Review

[ONOS-3946] Implement IcmpHandler for OpenstackRoutingService

- Process Icmp packet sent from Host to external network for OpenstackGateway Node
- Process Arp packet sent from physical  router to gateway

Change-Id: Ifcde71a9ca10180682811c9e1bcf58f991b36443
......@@ -68,7 +68,6 @@ public class OpenstackRouterCodec extends JsonCodec<OpenstackRouter> {
String tenantId = checkNotNull(routerInfo.path(TENANT_ID).asText());
String id = checkNotNull(routerInfo.path(ID).asText());
String name = checkNotNull(routerInfo.path(NAME).asText());
String status = checkNotNull(routerInfo.path(STATUS).asText());
String adminStateUp = checkNotNull(routerInfo.path(ADMIN_STATE_UP).asText());
OpenstackExternalGateway.Builder osExtBuiler = new OpenstackExternalGateway.Builder();
......@@ -98,7 +97,7 @@ public class OpenstackRouterCodec extends JsonCodec<OpenstackRouter> {
.tenantId(tenantId)
.id(id)
.name(name)
.status(OpenstackRouter.RouterStatus.valueOf(status))
.status(OpenstackRouter.RouterStatus.ACTIVE)
.adminStateUp(Boolean.valueOf(adminStateUp))
.gatewayExternalInfo(osExtBuiler.build());
......
......@@ -27,12 +27,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Contains OpenstackPort Information.
*/
public class OpenstackPortInfo {
public final class OpenstackPortInfo {
private final Ip4Address hostIp;
private final MacAddress hostMac;
private final DeviceId deviceId;
private final long vni;
private final Ip4Address gatewayIP;
private final String networkId;
private final Collection<String> securityGroups;
/**
......@@ -46,12 +47,13 @@ public class OpenstackPortInfo {
* @param securityGroups security group list
*/
public OpenstackPortInfo(Ip4Address hostIp, MacAddress hostMac, DeviceId deviceId, long vni,
Ip4Address gatewayIP, Collection<String> securityGroups) {
Ip4Address gatewayIP, String networkId, Collection<String> securityGroups) {
this.hostIp = hostIp;
this.hostMac = hostMac;
this.deviceId = deviceId;
this.vni = vni;
this.gatewayIP = gatewayIP;
this.networkId = networkId;
this.securityGroups = securityGroups;
}
......@@ -101,6 +103,15 @@ public class OpenstackPortInfo {
}
/**
* Returns network ID.
*
* @return network ID
*/
public String networkId() {
return networkId;
}
/**
* Returns Security Group ID list.
*
* @return list of Security Group ID
......@@ -129,11 +140,12 @@ public class OpenstackPortInfo {
private long vni;
private Ip4Address gatewayIP;
private Collection<String> securityGroups;
private String networkId;
/**
* Sets the IP address of the port.
*
* @param gatewayIP
* @param gatewayIP gateway IP
* @return Builder reference
*/
public Builder setGatewayIP(Ip4Address gatewayIP) {
......@@ -142,6 +154,17 @@ public class OpenstackPortInfo {
}
/**
* Sets the network ID.
*
* @param networkId network id
* @return Builder reference
*/
public Builder setNetworkId(String networkId) {
this.networkId = checkNotNull(networkId, "networkId cannot be null");
return this;
}
/**
* Sets the host IP address of the port.
*
* @param hostIp host IP address
......@@ -202,16 +225,7 @@ public class OpenstackPortInfo {
* @return OpenstackPortInfo reference
*/
public OpenstackPortInfo build() {
return new OpenstackPortInfo(this);
return new OpenstackPortInfo(hostIp, hostMac, deviceId, vni, gatewayIP, networkId, securityGroups);
}
}
private OpenstackPortInfo(Builder builder) {
hostIp = builder.hostIp;
hostMac = builder.hostMac;
deviceId = builder.deviceId;
vni = builder.vni;
gatewayIP = builder.gatewayIP;
securityGroups = builder.securityGroups;
}
}
......
{
"apps" : {
"org.onosproject.openstackswitching" : {
"openstackswitching" : {
"do_not_push_flows" : "false",
"neutron_server" : "http://192.168.56.103:9696/v2.0/",
"keystone_server" : "http://192.168.56.103:5000/v2.0/",
"user_name" : "admin",
"password" : "nova",
"physicalRouterMac" : "00:00:00:00:00:20",
"org.onosproject.openstackrouting" : {
"openstackrouting" : {
"physicalRouterMac" : "2a:a1:8a:89:dd:a4",
"gatewayBridgeId" : "of:0000000000000003",
"gatewayExternalInterfaceName" : "veth0",
"gatewayExternalInterfaceMac" : "be:15:c6:b0:df:9f"
}
},
"org.onosproject.openstacknode" : {
"openstacknode" : {
"nodes" : [
{
"hostname" : "compute-01",
"ovsdbIp" : "192.168.56.102",
"ovsdbPort" : "6640",
"bridgeId" : "of:0000000000000001",
"openstackNodeType" : "COMPUTENODE"
},
{
"hostname" : "compute-02",
"ovsdbIp" : "192.168.56.101",
"ovsdbPort" : "6640",
"bridgeId" : "of:0000000000000002",
"openstackNodeType" : "COMPUTENODE"
},
{
"hostname" : "network",
"ovsdbIp" : "192.168.56.106",
"ovsdbPort" : "6640",
"bridgeId" : "of:0000000000000003",
"openstackNodeType" : "GATEWAYNODE",
"externalIfName" : "eth3",
"externalIfMacAddress" : "00:00:00:00:00:11"
}
{
"hostname" : "compute-01",
"ovsdbIp" : "192.168.56.112",
"ovsdbPort" : "6640",
"bridgeId" : "of:0000000000000001",
"openstackNodeType" : "COMPUTENODE"
},
{
"hostname" : "compute-02",
"ovsdbIp" : "192.168.56.113",
"ovsdbPort" : "6640",
"bridgeId" : "of:0000000000000002",
"openstackNodeType" : "COMPUTENODE"
},
{
"hostname" : "network",
"ovsdbIp" : "192.168.56.114",
"ovsdbPort" : "6640",
"bridgeId" : "of:0000000000000003",
"openstackNodeType" : "GATEWAYNODE",
"gatewayExternalInterfaceName" : "veth0",
"gatewayExternalInterfaceMac" : "be:15:c6:b0:df:9f"
}
]
}
}
}
},
"org.onosproject.openstackinterface" : {
"openstackinterface" : {
"neutron_server" : "http://192.168.56.111:9696/v2.0/",
"keystone_server" : "http://192.168.56.111:5000/v2.0/",
"user_name" : "admin",
"password" : "nova"
}
}
},
"devices" : {
"of:0000000000000001" : {
......@@ -49,3 +59,4 @@
}
}
}
......
......@@ -5,7 +5,7 @@
* 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
* 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,
......@@ -15,21 +15,260 @@
*/
package org.onosproject.openstacknetworking.routing;
import com.google.common.collect.Maps;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.net.packet.PacketService;
import org.onosproject.openstackinterface.OpenstackInterfaceService;
import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstacknetworking.OpenstackPortInfo;
import org.onosproject.openstacknetworking.OpenstackSwitchingService;
import org.slf4j.Logger;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Handle ICMP packet processing for Managing Flow Rules In Openstack Nodes.
* Handle ICMP packet sent from Openstack Gateway nodes.
*/
public class OpenstackIcmpHandler implements Runnable {
public class OpenstackIcmpHandler {
protected final Logger log = getLogger(getClass());
private final PacketService packetService;
private final DeviceService deviceService;
private final Map<String, OpenstackPortInfo> icmpInfoMap = Maps.newHashMap();
private final OpenstackSwitchingService openstackSwitchingService;
private final OpenstackInterfaceService openstackService;
private final OpenstackRoutingConfig config;
private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
private static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
private static final String PORTNAME = "portName";
/**
* Default constructor.
*
* @param packetService packet service
* @param deviceService device service
* @param openstackService openstackInterface service
* @param config openstackRoutingConfig
* @param openstackSwitchingService openstackSwitching service
*/
OpenstackIcmpHandler(PacketService packetService, DeviceService deviceService,
OpenstackInterfaceService openstackService, OpenstackRoutingConfig config,
OpenstackSwitchingService openstackSwitchingService) {
this.packetService = packetService;
this.deviceService = deviceService;
this.openstackService = checkNotNull(openstackService);
this.config = checkNotNull(config);
this.openstackSwitchingService = checkNotNull(openstackSwitchingService);
}
/**
* Requests ICMP packet.
*
* @param appId Application Id
*/
public void requestPacket(ApplicationId appId) {
TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_ICMP)
.build();
packetService.requestPackets(icmpSelector,
PacketPriority.CONTROL,
appId,
Optional.of(DeviceId.deviceId(config.gatewayBridgeId())));
}
/**
* Handles ICMP packet.
*
* @param context packet context
* @param ethernet ethernet
*/
public void processIcmpPacket(PacketContext context, Ethernet ethernet) {
checkNotNull(context, "context can not be null");
checkNotNull(ethernet, "ethernet can not be null");
IPv4 ipPacket = (IPv4) ethernet.getPayload();
log.debug("icmpEvent called from ip {}, mac {}", Ip4Address.valueOf(ipPacket.getSourceAddress()).toString(),
ethernet.getSourceMAC().toString());
ICMP icmp = (ICMP) ipPacket.getPayload();
short icmpId = getIcmpId(icmp);
DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
//TODO: Considers icmp between internal subnets which are belonged to the same router.
volatile PacketContext context;
private OpenstackRoutingRulePopulator rulePopulator;
OpenstackIcmpHandler(OpenstackRoutingRulePopulator rulePopulator, PacketContext context) {
this.context = context;
this.rulePopulator = rulePopulator;
OpenstackPortInfo openstackPortInfo =
getOpenstackPortInfo(Ip4Address.valueOf(ipPacket.getSourceAddress()), ethernet.getSourceMAC());
checkNotNull(openstackPortInfo, "openstackPortInfo can not be null");
if (ipPacket.getDestinationAddress() == openstackPortInfo.gatewayIP().toInt()) {
processIcmpPacketSentToGateway(ipPacket, icmp, openstackPortInfo);
} else {
Ip4Address pNatIpAddress = pNatIpForPort(openstackPortInfo);
checkNotNull(pNatIpAddress, "pNatIpAddress can not be null");
sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress);
String icmpInfoKey = String.valueOf(icmpId)
.concat(String.valueOf(pNatIpAddress.toInt()))
.concat(String.valueOf(ipPacket.getDestinationAddress()));
icmpInfoMap.putIfAbsent(icmpInfoKey, openstackPortInfo);
}
} else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) {
String icmpInfoKey = String.valueOf(icmpId)
.concat(String.valueOf(ipPacket.getDestinationAddress()))
.concat(String.valueOf(ipPacket.getSourceAddress()));
processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey));
icmpInfoMap.remove(icmpInfoKey);
}
}
@Override
public void run() {
private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest,
OpenstackPortInfo openstackPortInfo) {
icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY)
.resetChecksum();
icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
.setDestinationAddress(openstackPortInfo.ip().toInt())
.resetChecksum();
icmpRequestIpv4.setPayload(icmpRequest);
Ethernet icmpResponseEth = new Ethernet();
icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
.setSourceMACAddress(GATEWAY_MAC)
.setDestinationMACAddress(openstackPortInfo.mac())
.setPayload(icmpRequestIpv4);
sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
}
private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId,
Ip4Address pNatIpAddress) {
icmpRequest.resetChecksum();
icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt())
.resetChecksum();
icmpRequestIpv4.setPayload(icmpRequest);
Ethernet icmpRequestEth = new Ethernet();
icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
.setSourceMACAddress(MacAddress.valueOf(config.gatewayExternalInterfaceMac()))
.setDestinationMACAddress(MacAddress.valueOf(config.physicalRouterMac()))
.setPayload(icmpRequestIpv4);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(getPortForAnnotationPortName(DeviceId.deviceId(config.gatewayBridgeId()),
config.gatewayExternalInterfaceName()))
.build();
OutboundPacket packet = new DefaultOutboundPacket(deviceId,
treatment, ByteBuffer.wrap(icmpRequestEth.serialize()));
packetService.emit(packet);
}
private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse,
OpenstackPortInfo openstackPortInfo) {
icmpResponse.resetChecksum();
icmpResponseIpv4.setDestinationAddress(openstackPortInfo.ip().toInt())
.resetChecksum();
icmpResponseIpv4.setPayload(icmpResponse);
Ethernet icmpResponseEth = new Ethernet();
icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
.setSourceMACAddress(GATEWAY_MAC)
.setDestinationMACAddress(openstackPortInfo.mac())
.setPayload(icmpResponseIpv4);
sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
}
private void sendResponsePacketToHost(Ethernet icmpResponseEth, OpenstackPortInfo openstackPortInfo) {
Map.Entry<String, OpenstackPortInfo> entry = openstackSwitchingService.openstackPortInfo().entrySet().stream()
.filter(e -> e.getValue().mac().equals(openstackPortInfo.mac()))
.findAny().orElse(null);
if (entry == null) {
return;
}
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(getPortForAnnotationPortName(openstackPortInfo.deviceId(), entry.getKey()))
.build();
OutboundPacket packet = new DefaultOutboundPacket(openstackPortInfo.deviceId(),
treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
packetService.emit(packet);
}
private OpenstackPortInfo getOpenstackPortInfo(Ip4Address sourceIp, MacAddress sourceMac) {
checkNotNull(openstackSwitchingService.openstackPortInfo(), "openstackportinfo collection can not be null");
return openstackSwitchingService.openstackPortInfo().values()
.stream().filter(p -> p.ip().equals(sourceIp) && p.mac().equals(sourceMac))
.findAny().orElse(null);
}
private short getIcmpId(ICMP icmp) {
return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
}
private Ip4Address pNatIpForPort(OpenstackPortInfo openstackPortInfo) {
OpenstackPort openstackPort = openstackService.ports().stream()
.filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) &&
p.networkId().equals(openstackPortInfo.networkId()))
.findAny().orElse(null);
checkNotNull(openstackPort, "openstackPort can not be null");
return openstackService.router(openstackPort.deviceId())
.gatewayExternalInfo().externalFixedIps().values()
.stream().findAny().orElse(null);
}
private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) {
Port port = deviceService.getPorts(deviceId).stream()
.filter(p -> p.annotations().value(PORTNAME).equals(match))
.findAny().orElse(null);
checkNotNull(port, "port cannot be null");
return port.number();
}
}
\ No newline at end of file
}
......
/*
* Copyright 2016 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.onosproject.openstacknetworking.routing;
import org.onlab.packet.ARP;
import org.onlab.packet.EthType;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.net.packet.PacketService;
import org.onosproject.openstackinterface.OpenstackInterfaceService;
import org.onosproject.openstackinterface.OpenstackPort;
import org.slf4j.Logger;
import java.nio.ByteBuffer;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Handle ARP packet sent from Openstack Gateway nodes.
*/
public class OpenstackRoutingArpHandler {
protected final Logger log = getLogger(getClass());
private final PacketService packetService;
private final OpenstackInterfaceService openstackService;
private final OpenstackRoutingConfig config;
private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway";
/**
* Default constructor.
*
* @param packetService packet service
* @param openstackService openstackInterface service
* @param config openstackRoutingConfig
*/
OpenstackRoutingArpHandler(PacketService packetService, OpenstackInterfaceService openstackService,
OpenstackRoutingConfig config) {
this.packetService = packetService;
this.openstackService = checkNotNull(openstackService);
this.config = checkNotNull(config);
}
/**
* Requests ARP packet to GatewayNode.
*
* @param appId application id
*/
public void requestPacket(ApplicationId appId) {
TrafficSelector arpSelector = DefaultTrafficSelector.builder()
.matchEthType(EthType.EtherType.ARP.ethType().toShort())
.build();
packetService.requestPackets(arpSelector,
PacketPriority.CONTROL,
appId,
Optional.of(DeviceId.deviceId(config.gatewayBridgeId())));
}
/**
* Handles ARP packet.
*
* @param context packet context
* @param ethernet ethernet
*/
public void processArpPacketFromRouter(PacketContext context, Ethernet ethernet) {
checkNotNull(context, "context can not be null");
checkNotNull(ethernet, "ethernet can not be null");
log.info("arpEvent called from {} to {}",
Ip4Address.valueOf(((IPv4) ethernet.getPayload()).getSourceAddress()).toString(),
Ip4Address.valueOf(((IPv4) ethernet.getPayload()).getDestinationAddress()).toString());
ARP arp = (ARP) ethernet.getPayload();
if (arp.getOpCode() != ARP.OP_REQUEST) {
return;
}
IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
MacAddress targetMac = getTargetMacForTargetIp(targetIp.getIp4Address());
if (targetMac == MacAddress.NONE) {
return;
}
Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
targetMac, ethernet);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(context.inPacket().receivedFrom().port())
.build();
packetService.emit(new DefaultOutboundPacket(
context.inPacket().receivedFrom().deviceId(),
treatment,
ByteBuffer.wrap(ethReply.serialize())));
}
private MacAddress getTargetMacForTargetIp(Ip4Address targetIp) {
OpenstackPort port = openstackService.ports().stream()
.filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY))
.filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address()))
.findAny().orElse(null);
if (port == null) {
return MacAddress.NONE;
}
return port.macAddress();
}
}
/*
* Copyright 2016 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.onosproject.openstacknetworking.routing;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.config.Config;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Configuration object for OpenstackRouting service.
*/
public class OpenstackRoutingConfig extends Config<ApplicationId> {
protected final Logger log = getLogger(getClass());
public static final String PHYSICAL_ROUTER_MAC = "physicalRouterMac";
public static final String GATEWAY_BRIDGE_ID = "gatewayBridgeId";
public static final String GATEWAY_EXTERNAL_INTERFACE_NAME = "gatewayExternalInterfaceName";
public static final String GATEWAY_EXTERNAL_INTERFACE_MAC = "gatewayExternalInterfaceMac";
/**
* Returns physical router mac.
*
* @return physical router mac
*/
public String physicalRouterMac() {
return this.get("physicalRouterMac", "");
}
/**
* Returns gateway's bridge id.
*
* @return bridge id
*/
public String gatewayBridgeId() {
return this.get("gatewayBridgeId", "");
}
/**
* Returns gateway's external interface name.
*
* @return external interface name
*/
public String gatewayExternalInterfaceName() {
return this.get("gatewayExternalInterfaceName", "");
}
/**
* Returns gateway's external interface mac.
*
* @return external interface mac
*/
public String gatewayExternalInterfaceMac() {
return this.get("gatewayExternalInterfaceMac", "");
}
}
......@@ -32,6 +32,12 @@ import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.flowobjective.FlowObjectiveService;
......@@ -45,6 +51,7 @@ import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstackinterface.OpenstackRouter;
import org.onosproject.openstackinterface.OpenstackRouterInterface;
import org.onosproject.openstacknetworking.OpenstackRoutingService;
import org.onosproject.openstacknetworking.OpenstackSwitchingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -80,24 +87,44 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
protected OpenstackInterfaceService openstackService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackSwitchingService openstackSwitchingService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowObjectiveService flowObjectiveService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService configService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry configRegistry;
private ApplicationId appId;
private Map<String, OpenstackRouterInterface> routerInterfaceMap = Maps.newHashMap();
private Map<Integer, String> portNumMap = initPortNumMap();
private static final String APP_ID = "org.onosproject.openstackrouting";
private static final String PORT_NAME = "portName";
private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
private final ConfigFactory configFactory =
new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackRoutingConfig.class, "openstackrouting") {
@Override
public OpenstackRoutingConfig createConfig() {
return new OpenstackRoutingConfig();
}
};
private final NetworkConfigListener configListener = new InternalConfigListener();
// TODO: This will be replaced to get the information from openstackswitchingservice.
private static final String EXTERNAL_INTERFACE_NAME = "veth0";
private OpenstackRoutingConfig config;
private static final int PNAT_PORT_NUM_START = 1024;
private static final int PNAT_PORT_NUM_END = 65535;
private Map<Integer, String> initPortNumMap() {
Map<Integer, String> map = Maps.newHashMap();
for (int i = 1024; i < 65535; i++) {
for (int i = PNAT_PORT_NUM_START; i < PNAT_PORT_NUM_END; i++) {
map.put(i, "");
}
return map;
......@@ -108,17 +135,30 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event"));
private ExecutorService icmpEventExecutorService =
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "icmp-event"));
private ExecutorService arpEventExecutorService =
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event"));
private OpenstackIcmpHandler openstackIcmpHandler;
private OpenstackRoutingArpHandler openstackArpHandler;
@Activate
protected void activate() {
appId = coreService.registerApplication(APP_ID);
packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
configRegistry.registerConfigFactory(configFactory);
configService.addListener(configListener);
readConfiguration();
log.info("onos-openstackrouting started");
}
@Deactivate
protected void deactivate() {
packetService.removeProcessor(internalPacketProcessor);
l3EventExecutorService.shutdown();
icmpEventExecutorService.shutdown();
arpEventExecutorService.shutdown();
log.info("onos-openstackrouting stopped");
}
......@@ -155,16 +195,16 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
@Override
public void updateRouterInterface(OpenstackRouterInterface routerInterface) {
routerInterfaceMap.putIfAbsent(routerInterface.portId(), routerInterface);
routerInterfaceMap.putIfAbsent(routerInterface.id(), routerInterface);
List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
routerInterfaces.add(routerInterface);
checkExternalConnection(getOpenstackRouter(routerInterface.portId()), routerInterfaces);
checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
}
@Override
public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
openstackService, flowObjectiveService, deviceService, driverService);
openstackService, flowObjectiveService, deviceService, driverService, config);
rulePopulator.removeExternalRules(routerInterface);
routerInterfaceMap.remove(routerInterface.portId());
}
......@@ -181,10 +221,10 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) {
OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder()
.id(checkNotNull(p.id()))
.id(checkNotNull(p.deviceId()))
.tenantId(checkNotNull(openstackService.network(p.networkId()).tenantId()))
.subnetId(checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString())
.portId(checkNotNull(p.deviceId()));
.portId(checkNotNull(p.id()));
return osBuilder.build();
}
......@@ -196,19 +236,26 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
if (context.isHandled()) {
return;
} else if (!context.inPacket().receivedFrom().deviceId().toString()
.equals(config.gatewayBridgeId())) {
return;
}
InboundPacket pkt = context.inPacket();
Ethernet ethernet = pkt.parsed();
if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
//TODO: Considers IPv6 later.
if (ethernet == null) {
return;
} else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
IPv4 iPacket = (IPv4) ethernet.getPayload();
OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
openstackService, flowObjectiveService, deviceService,
driverService);
openstackService, flowObjectiveService, deviceService, driverService, config);
switch (iPacket.getProtocol()) {
case IPv4.PROTOCOL_ICMP:
icmpEventExecutorService.execute(new OpenstackIcmpHandler(rulePopulator, context));
icmpEventExecutorService.submit(() ->
openstackIcmpHandler.processIcmpPacket(context, ethernet));
break;
case IPv4.PROTOCOL_UDP:
// don't process DHCP
......@@ -219,7 +266,8 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
}
default:
int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
Port port = getExternalPort(pkt.receivedFrom().deviceId(), EXTERNAL_INTERFACE_NAME);
Port port =
getExternalPort(pkt.receivedFrom().deviceId(), config.gatewayExternalInterfaceName());
if (port == null) {
log.warn("There`s no external interface");
break;
......@@ -230,7 +278,9 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
portNum, openstackPort, port));
break;
}
} else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
arpEventExecutorService.submit(() ->
openstackArpHandler.processArpPacketFromRouter(context, ethernet));
}
}
......@@ -265,9 +315,9 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
private void initiateL3Rule(OpenstackRouter router, OpenstackRouterInterface routerInterface) {
long vni = Long.parseLong(openstackService.network(openstackService
.port(routerInterface.id()).networkId()).segmentId());
.port(routerInterface.portId()).networkId()).segmentId());
OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
openstackService, flowObjectiveService, deviceService, driverService);
openstackService, flowObjectiveService, deviceService, driverService, config);
rulePopulator.populateExternalRules(vni, router, routerInterface);
}
......@@ -288,4 +338,50 @@ public class OpenstackRoutingManager implements OpenstackRoutingService {
.equals(ip4Address) ? openstackPort : null;
}
private void readConfiguration() {
config = configService.getConfig(appId, OpenstackRoutingConfig.class);
if (config == null) {
log.error("No configuration found");
return;
}
checkNotNull(config.physicalRouterMac());
checkNotNull(config.gatewayBridgeId());
checkNotNull(config.gatewayExternalInterfaceMac());
checkNotNull(config.gatewayExternalInterfaceName());
log.debug("Configured info: {}, {}, {}, {}", config.physicalRouterMac(), config.gatewayBridgeId(),
config.gatewayExternalInterfaceMac(), config.gatewayExternalInterfaceName());
reloadInitL3Rules();
openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService,
openstackService, config, openstackSwitchingService);
openstackArpHandler = new OpenstackRoutingArpHandler(packetService, openstackService, config);
openstackIcmpHandler.requestPacket(appId);
openstackArpHandler.requestPacket(appId);
log.info("OpenstackRouting configured");
}
private class InternalConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
if (!event.configClass().equals(OpenstackRoutingConfig.class)) {
return;
}
switch (event.type()) {
case CONFIG_ADDED:
case CONFIG_UPDATED:
l3EventExecutorService.execute(OpenstackRoutingManager.this::readConfiguration);
break;
default:
log.debug("Unsupported event type {}", event.type().toString());
break;
}
}
}
}
......
......@@ -71,6 +71,7 @@ public class OpenstackRoutingRulePopulator {
private final OpenstackInterfaceService openstackService;
private final DeviceService deviceService;
private final DriverService driverService;
private final OpenstackRoutingConfig config;
private static final String PORTNAME_PREFIX_VM = "tap";
private static final String PORTNAME_PREFIX_ROUTER = "qr";
......@@ -93,9 +94,6 @@ public class OpenstackRoutingRulePopulator {
private OpenstackRouter router;
private OpenstackRouterInterface routerInterface;
// TODO: This will be replaced to get the information from openstackswitchingservice.
private static final String EXTERNAL_INTERFACE_NAME = "veth0";
/**
* The constructor of openstackRoutingRulePopulator.
*
......@@ -104,20 +102,23 @@ public class OpenstackRoutingRulePopulator {
* @param flowObjectiveService FlowObjectiveService
* @param deviceService DeviceService
* @param driverService DriverService
* @param config OpenstackRoutingConfig
*/
public OpenstackRoutingRulePopulator(ApplicationId appId, OpenstackInterfaceService openstackService,
FlowObjectiveService flowObjectiveService,
DeviceService deviceService, DriverService driverService) {
FlowObjectiveService flowObjectiveService, DeviceService deviceService,
DriverService driverService, OpenstackRoutingConfig config) {
this.appId = appId;
this.flowObjectiveService = flowObjectiveService;
this.openstackService = openstackService;
this.openstackService = checkNotNull(openstackService);
this.deviceService = deviceService;
this.driverService = driverService;
this.config = config;
}
/**
* Populates flow rules for Pnat configurations.
* @param inboundPacket Packet-in event packet
*
* @param inboundPacket Packet-in event packet
* @param openstackPort Target VM information
* @param portNum Pnat port number
* @param externalIp external ip address
......@@ -168,6 +169,7 @@ public class OpenstackRoutingRulePopulator {
tBuilder.setUdpSrc(TpPort.tpPort(portNum));
break;
default:
log.debug("Unsupported IPv4 protocol {}");
break;
}
......@@ -188,7 +190,7 @@ public class OpenstackRoutingRulePopulator {
private Port getPortNumOfExternalInterface() {
return deviceService.getPorts(inboundPacket.receivedFrom().deviceId()).stream()
.filter(p -> p.annotations().value(PORTNAME).equals(EXTERNAL_INTERFACE_NAME))
.filter(p -> p.annotations().value(PORTNAME).equals(config.gatewayExternalInterfaceName()))
.findAny().orElse(null);
}
......@@ -239,7 +241,15 @@ public class OpenstackRoutingRulePopulator {
flowObjectiveService.forward(inboundPacket.receivedFrom().deviceId(), fo);
}
private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
/**
* Returns NiciraExtension treatment.
*
* @param id device id
* @param hostIp host ip
* @return NiciraExtension treatment
*/
public ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
Driver driver = driverService.getDriver(id);
DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
......@@ -257,7 +267,13 @@ public class OpenstackRoutingRulePopulator {
return extensionInstruction;
}
private PortNumber getTunnelPort(DeviceId deviceId) {
/**
* Returns port number of vxlan tunnel.
*
* @param deviceId device id
* @return port number of vxlan tunnel
*/
public PortNumber getTunnelPort(DeviceId deviceId) {
Port port = deviceService.getPorts(deviceId).stream()
.filter(p -> p.annotations().value(PORTNAME).equals(PORTNAME_PREFIX_TUNNEL))
.findAny().orElse(null);
......@@ -343,16 +359,11 @@ public class OpenstackRoutingRulePopulator {
}
private Device getGatewayNode() {
return checkNotNull(StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
.filter(d -> checkGatewayNode(d.id()))
.findAny()
.orElse(null));
return checkNotNull(deviceService.getDevice(DeviceId.deviceId(config.gatewayBridgeId())));
}
private boolean checkGatewayNode(DeviceId deviceId) {
return !deviceService.getPorts(deviceId).stream().anyMatch(port ->
port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_ROUTER) ||
port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_VM));
return deviceId.toString().equals(config.gatewayBridgeId());
}
private long getVni(OpenstackPort openstackPort) {
......
......@@ -94,6 +94,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackInterfaceService openstackService;
public static final String PORTNAME_PREFIX_VM = "tap";
......@@ -126,6 +127,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
deviceService.addListener(internalDeviceListener);
hostService.addListener(internalHostListener);
arpHandler = new OpenstackArpHandler(openstackService, packetService, hostService);
arpHandler = new OpenstackArpHandler(openstackService, packetService, hostService);
sgRulePopulator = new OpenstackSecurityGroupRulePopulator(appId, openstackService, flowObjectiveService);
......@@ -204,10 +206,12 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
@Override
public void createNetwork(OpenstackNetwork openstackNetwork) {
//TODO
}
@Override
public void createSubnet(OpenstackSubnet openstackSubnet) {
//TODO
}
@Override
......@@ -296,9 +300,17 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
private void updatePortMap(DeviceId deviceId, String portName, Collection<OpenstackNetwork> networks,
Collection<OpenstackSubnet> subnets, OpenstackPort openstackPort) {
long vni = Long.parseLong(networks.stream()
long vni;
OpenstackNetwork openstackNetwork = networks.stream()
.filter(n -> n.id().equals(openstackPort.networkId()))
.findAny().orElse(null).segmentId());
.findAny().orElse(null);
if (openstackNetwork != null) {
vni = Long.parseLong(openstackNetwork.segmentId());
} else {
log.debug("updatePortMap failed because there's no OpenstackNetwork matches {}", openstackPort.networkId());
return;
}
OpenstackSubnet openstackSubnet = subnets.stream()
.filter(n -> n.networkId().equals(openstackPort.networkId()))
......@@ -312,6 +324,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
.setHostMac(openstackPort.macAddress())
.setVni(vni)
.setGatewayIP(gatewayIPAddress)
.setNetworkId(openstackPort.networkId())
.setSecurityGroups(openstackPort.securityGroups());
openstackPortInfoMap.put(portName, portBuilder.build());
......@@ -321,7 +334,6 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
securityGroupMap.put(sgId, openstackService.getSecurityGroup(sgId));
}
});
}
private void processHostRemoved(Host host) {
......@@ -440,6 +452,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
processPortRemoved((Device) deviceEvent.subject(), deviceEvent.port());
break;
default:
log.debug("Unsupported deviceEvent type {}", deviceEvent.type().toString());
break;
}
} else if (event instanceof HostEvent) {
......@@ -450,6 +463,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
processHostRemoved((Host) hostEvent.subject());
break;
default:
log.debug("Unsupported hostEvent type {}", hostEvent.type().toString());
break;
}
}
......