| /* |
| * Copyright 2016-present Open Networking Foundation |
| * |
| * 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.segmentrouting; |
| |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.IpAddress; |
| import org.onlab.packet.MacAddress; |
| import org.onosproject.net.neighbour.NeighbourMessageContext; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.Host; |
| import org.onosproject.net.HostId; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.host.HostService; |
| import org.onosproject.net.packet.DefaultOutboundPacket; |
| import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; |
| import org.onosproject.segmentrouting.config.DeviceConfiguration; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.nio.ByteBuffer; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| /** |
| * This handler provides provides useful functions to the |
| * neighbour handlers (ARP, NDP). |
| */ |
| public class SegmentRoutingNeighbourHandler { |
| |
| private static Logger log = LoggerFactory.getLogger(SegmentRoutingNeighbourHandler.class); |
| |
| protected SegmentRoutingManager srManager; |
| protected DeviceConfiguration config; |
| |
| /** |
| * Creates an SegmentRoutingNeighbourHandler object. |
| * |
| * @param srManager SegmentRoutingManager object |
| */ |
| public SegmentRoutingNeighbourHandler(SegmentRoutingManager srManager) { |
| this.srManager = srManager; |
| this.config = checkNotNull(srManager.deviceConfiguration); |
| } |
| |
| /** |
| * Creates an SegmentRoutingNeighbourHandler object. |
| */ |
| public SegmentRoutingNeighbourHandler() { |
| this.srManager = null; |
| this.config = null; |
| } |
| |
| /** |
| * Retrieve router (device) info. |
| * |
| * @param mac where to copy the mac |
| * @param ip where to copy the ip |
| * @param deviceId the device id |
| * @param targetAddress the target address |
| * @return true if it was possible to get the necessary info. |
| * False for errors |
| */ |
| protected boolean getSenderInfo(byte[] mac, |
| byte[] ip, |
| DeviceId deviceId, |
| IpAddress targetAddress) { |
| byte[] senderMacAddress; |
| byte[] senderIpAddress; |
| IpAddress sender; |
| try { |
| senderMacAddress = config.getDeviceMac(deviceId).toBytes(); |
| if (targetAddress.isIp4()) { |
| sender = config.getRouterIpAddressForASubnetHost(targetAddress.getIp4Address()); |
| } else { |
| sender = config.getRouterIpAddressForASubnetHost(targetAddress.getIp6Address()); |
| } |
| // If sender is null we abort. |
| if (sender == null) { |
| log.warn("Sender ip is null. Aborting getSenderInfo"); |
| return false; |
| } |
| senderIpAddress = sender.toOctets(); |
| } catch (DeviceConfigNotFoundException e) { |
| log.warn(e.getMessage() + " Aborting getSenderInfo"); |
| return false; |
| } |
| System.arraycopy(senderMacAddress, 0, mac, 0, senderMacAddress.length); |
| System.arraycopy(senderIpAddress, 0, ip, 0, senderIpAddress.length); |
| return true; |
| } |
| |
| /** |
| * Utility to send a ND reply using the supplied information. |
| * |
| * @param pkt the request |
| * @param targetMac the target mac |
| * @param hostService the host service |
| */ |
| protected void sendResponse(NeighbourMessageContext pkt, MacAddress targetMac, HostService hostService) { |
| HostId dstId = HostId.hostId(pkt.srcMac(), pkt.vlan()); |
| Host dst = hostService.getHost(dstId); |
| if (dst == null) { |
| log.warn("Cannot send {} response to host {} - does not exist in the store", |
| pkt.protocol(), dstId); |
| return; |
| } |
| pkt.reply(targetMac); |
| } |
| |
| /** |
| * Flood to all ports in the same subnet. |
| * |
| * @param packet packet to be flooded |
| * @param inPort where the packet comes from |
| * @param targetAddress the target address |
| */ |
| protected void flood(Ethernet packet, ConnectPoint inPort, IpAddress targetAddress) { |
| try { |
| srManager.deviceConfiguration |
| .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> { |
| if (subnet.contains(targetAddress)) { |
| ports.stream() |
| .filter(port -> port != inPort.port()) |
| .forEach(port -> { |
| forward(packet, new ConnectPoint(inPort.deviceId(), port)); |
| }); |
| } |
| }); |
| } catch (DeviceConfigNotFoundException e) { |
| log.warn(e.getMessage() |
| + " Cannot flood in subnet as device config not available" |
| + " for device: " + inPort.deviceId()); |
| } |
| } |
| |
| /* |
| * Floods only on the port which have been configured with the subnet |
| * of the target address. The in port is excluded. |
| * |
| * @param pkt the ndp/arp packet and context information |
| */ |
| protected void flood(NeighbourMessageContext pkt) { |
| try { |
| srManager.deviceConfiguration |
| .getSubnetPortsMap(pkt.inPort().deviceId()).forEach((subnet, ports) -> { |
| if (subnet.contains(pkt.target())) { |
| ports.stream() |
| .filter(port -> port != pkt.inPort().port()) |
| .forEach(port -> { |
| ConnectPoint outPoint = new ConnectPoint( |
| pkt.inPort().deviceId(), |
| port |
| ); |
| pkt.forward(outPoint); |
| }); |
| } |
| }); |
| } catch (DeviceConfigNotFoundException e) { |
| log.warn(e.getMessage() |
| + " Cannot flood in subnet as device config not available" |
| + " for device: " + pkt.inPort().deviceId()); |
| } |
| } |
| |
| /** |
| * Packet out to given port. |
| * |
| * Note: In current implementation, we expect all communication with |
| * end hosts within a subnet to be untagged. |
| * <p> |
| * For those pipelines that internally assigns a VLAN, the VLAN tag will be |
| * removed before egress. |
| * <p> |
| * For those pipelines that do not assign internal VLAN, the packet remains |
| * untagged. |
| * |
| * @param packet packet to be forwarded |
| * @param outPort where the packet should be forwarded |
| */ |
| private void forward(Ethernet packet, ConnectPoint outPort) { |
| ByteBuffer buf = ByteBuffer.wrap(packet.serialize()); |
| |
| TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); |
| tbuilder.setOutput(outPort.port()); |
| srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), |
| tbuilder.build(), buf)); |
| } |
| |
| } |