/* * Copyright 2016-present 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.incubator.net.neighbour.impl; import org.onlab.packet.ARP; import org.onlab.packet.Ethernet; import org.onlab.packet.ICMP6; import org.onlab.packet.IPv6; import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip6Address; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; import org.onlab.packet.ndp.NeighborAdvertisement; import org.onlab.packet.ndp.NeighborDiscoveryOptions; import org.onlab.util.Tools; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.neighbour.NeighbourMessageActions; import org.onosproject.incubator.net.neighbour.NeighbourMessageContext; import org.onosproject.net.ConnectPoint; import org.onosproject.net.edge.EdgePortService; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.packet.DefaultOutboundPacket; import org.onosproject.net.packet.PacketService; import java.nio.ByteBuffer; /** * Implementation of neighbour message actions. */ public class DefaultNeighbourMessageActions implements NeighbourMessageActions { private final EdgePortService edgeService; private final PacketService packetService; public DefaultNeighbourMessageActions(PacketService packetService, EdgePortService edgeService) { this.packetService = packetService; this.edgeService = edgeService; } @Override public void reply(NeighbourMessageContext context, MacAddress targetMac) { replyInternal(context, targetMac); } @Override public void forward(NeighbourMessageContext context, ConnectPoint outPort) { sendTo(context.packet(), outPort); } @Override public void forward(NeighbourMessageContext context, Interface outIntf) { Ethernet packetOut = (Ethernet) context.packet().clone(); if (outIntf.vlan().equals(VlanId.NONE)) { // The egress interface has no VLAN Id. Send out an untagged // packet packetOut.setVlanID(Ethernet.VLAN_UNTAGGED); } else { // The egress interface has a VLAN set. Send out a tagged packet packetOut.setVlanID(outIntf.vlan().toShort()); } sendTo(packetOut, outIntf.connectPoint()); } @Override public void flood(NeighbourMessageContext context) { Tools.stream(edgeService.getEdgePoints()) .filter(cp -> !cp.equals(context.inPort())) .forEach(cp -> sendTo(context.packet(), cp)); } @Override public void drop(NeighbourMessageContext context) { } private void replyInternal(NeighbourMessageContext context, MacAddress targetMac) { switch (context.protocol()) { case ARP: sendTo(ARP.buildArpReply((Ip4Address) context.target(), targetMac, context.packet()), context.inPort()); break; case NDP: sendTo(buildNdpReply((Ip6Address) context.target(), targetMac, context.packet()), context.inPort()); break; default: break; } } /** * Outputs a packet out a specific port. * * @param packet the packet to send * @param outPort the port to send it out */ private void sendTo(Ethernet packet, ConnectPoint outPort) { sendTo(ByteBuffer.wrap(packet.serialize()), outPort); } /** * Outputs a packet out a specific port. * * @param packet packet to send * @param outPort port to send it out */ private void sendTo(ByteBuffer 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(), packet)); } /** * Builds an NDP reply based on a request. * * @param srcIp the IP address to use as the reply source * @param srcMac the MAC address to use as the reply source * @param request the Neighbor Solicitation request we got * @return an Ethernet frame containing the Neighbor Advertisement reply */ private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac, Ethernet request) { Ethernet eth = new Ethernet(); eth.setDestinationMACAddress(request.getSourceMAC()); eth.setSourceMACAddress(srcMac); eth.setEtherType(Ethernet.TYPE_IPV6); eth.setVlanID(request.getVlanID()); IPv6 requestIp = (IPv6) request.getPayload(); IPv6 ipv6 = new IPv6(); ipv6.setSourceAddress(srcIp.toOctets()); ipv6.setDestinationAddress(requestIp.getSourceAddress()); ipv6.setHopLimit((byte) 255); ICMP6 icmp6 = new ICMP6(); icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT); icmp6.setIcmpCode((byte) 0); NeighborAdvertisement nadv = new NeighborAdvertisement(); nadv.setTargetAddress(srcIp.toOctets()); nadv.setSolicitedFlag((byte) 1); nadv.setOverrideFlag((byte) 1); nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS, srcMac.toBytes()); icmp6.setPayload(nadv); ipv6.setPayload(icmp6); eth.setPayload(ipv6); return eth; } }