private Ethernet buildArpRequest( IpAddress targetIp, IpAddress sourceIp, MacAddress sourceMac, VlanId vlan) { ARP arp = new ARP(); arp.setHardwareType(ARP.HW_TYPE_ETHERNET) .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH) .setProtocolType(ARP.PROTO_TYPE_IP) .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH) .setOpCode(ARP.OP_REQUEST); arp.setSenderHardwareAddress(sourceMac.toBytes()) .setSenderProtocolAddress(sourceIp.toOctets()) .setTargetHardwareAddress(ZERO_MAC_ADDRESS) .setTargetProtocolAddress(targetIp.toOctets()); Ethernet ethernet = new Ethernet(); ethernet .setEtherType(Ethernet.TYPE_ARP) .setDestinationMACAddress(MacAddress.BROADCAST) .setSourceMACAddress(sourceMac) .setPayload(arp); if (!vlan.equals(VlanId.NONE)) { ethernet.setVlanID(vlan.toShort()); } ethernet.setPad(true); return ethernet; }
/** * Sends an APR request for the target IP address to all ports except in-port. * * @param deviceId Switch device ID * @param targetAddress target IP address for ARP * @param inPort in-port */ public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) { byte[] senderMacAddress; byte[] senderIpAddress; try { senderMacAddress = config.getDeviceMac(deviceId).toBytes(); senderIpAddress = config.getRouterIp(deviceId).toOctets(); } catch (DeviceConfigNotFoundException e) { log.warn(e.getMessage() + " Aborting sendArpRequest."); return; } ARP arpRequest = new ARP(); arpRequest .setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(ARP.PROTO_TYPE_IP) .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH) .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH) .setOpCode(ARP.OP_REQUEST) .setSenderHardwareAddress(senderMacAddress) .setTargetHardwareAddress(MacAddress.ZERO.toBytes()) .setSenderProtocolAddress(senderIpAddress) .setTargetProtocolAddress(targetAddress.toOctets()); Ethernet eth = new Ethernet(); eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes()) .setSourceMACAddress(senderMacAddress) .setEtherType(Ethernet.TYPE_ARP) .setPayload(arpRequest); removeVlanAndFlood(eth, inPort); }
private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) { ARP arpReply = (ARP) payload.getPayload(); VlanId vlanId = VlanId.vlanId(payload.getVlanID()); HostId targetHostId = HostId.hostId(MacAddress.valueOf(arpReply.getTargetHardwareAddress()), vlanId); // ARP reply for router. Process all pending IP packets. if (isArpForRouter(deviceId, arpReply)) { Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress()); srManager.ipHandler.forwardPackets(deviceId, hostIpAddress); } else { Host targetHost = srManager.hostService.getHost(targetHostId); // ARP reply for known hosts. Forward to the host. if (targetHost != null) { removeVlanAndForward(payload, targetHost.location()); // ARP reply for unknown host, Flood in the subnet. } else { // Don't flood to non-edge ports if (vlanId.equals(VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) { return; } removeVlanAndFlood(payload, inPort); } } }
/** * Deserializer function for ARP packets. * * @return deserializer function */ public static Deserializer<ARP> deserializer() { return (data, offset, length) -> { checkInput(data, offset, length, INITIAL_HEADER_LENGTH); ARP arp = new ARP(); final ByteBuffer bb = ByteBuffer.wrap(data, offset, length); arp.setHardwareType(bb.getShort()); arp.setProtocolType(bb.getShort()); byte hwAddressLength = bb.get(); arp.setHardwareAddressLength(hwAddressLength); byte protocolAddressLength = bb.get(); arp.setProtocolAddressLength(protocolAddressLength); arp.setOpCode(bb.getShort()); // Check we have enough space for the addresses checkHeaderLength( length, INITIAL_HEADER_LENGTH + 2 * hwAddressLength + 2 * protocolAddressLength); arp.senderHardwareAddress = new byte[0xff & hwAddressLength]; bb.get(arp.senderHardwareAddress, 0, arp.senderHardwareAddress.length); arp.senderProtocolAddress = new byte[0xff & protocolAddressLength]; bb.get(arp.senderProtocolAddress, 0, arp.senderProtocolAddress.length); arp.targetHardwareAddress = new byte[0xff & hwAddressLength]; bb.get(arp.targetHardwareAddress, 0, arp.targetHardwareAddress.length); arp.targetProtocolAddress = new byte[0xff & protocolAddressLength]; bb.get(arp.targetProtocolAddress, 0, arp.targetProtocolAddress.length); return arp; }; }
@Test public void emit() { MacAddress mac1 = MacAddress.of("00:00:00:11:00:01"); MacAddress mac2 = MacAddress.of("00:00:00:22:00:02"); ARP arp = new ARP(); arp.setSenderProtocolAddress(ANY) .setSenderHardwareAddress(mac1.getBytes()) .setTargetHardwareAddress(mac2.getBytes()) .setTargetProtocolAddress(ANY) .setHardwareType((short) 0) .setProtocolType((short) 0) .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode((byte) 0); Ethernet eth = new Ethernet(); eth.setVlanID(VLANID) .setEtherType(Ethernet.TYPE_ARP) .setSourceMACAddress("00:00:00:11:00:01") .setDestinationMACAddress("00:00:00:22:00:02") .setPayload(arp); // the should-be working setup. OutboundPacket passPkt = outPacket(DID, TR, eth); sw.setRole(RoleState.MASTER); provider.emit(passPkt); assertEquals("invalid switch", sw, controller.current); assertEquals("message not sent", PLIST.size(), sw.sent.size()); sw.sent.clear(); // wrong Role // sw.setRole(RoleState.SLAVE); // provider.emit(passPkt); // assertEquals("invalid switch", sw, controller.current); // assertEquals("message sent incorrectly", 0, sw.sent.size()); // sw.setRole(RoleState.MASTER); // missing switch OutboundPacket swFailPkt = outPacket(DID_MISSING, TR, eth); provider.emit(swFailPkt); assertNull("invalid switch", controller.current); assertEquals("message sent incorrectly", 0, sw.sent.size()); // to missing port // OutboundPacket portFailPkt = outPacket(DID, TR_MISSING, eth); // provider.emit(portFailPkt); // assertEquals("extra message sent", 1, sw.sent.size()); }
@Override public InboundPacket inPacket() { ARP arp = new ARP(); arp.setSenderProtocolAddress(IP) .setSenderHardwareAddress(MAC.toBytes()) .setTargetHardwareAddress(BCMAC.toBytes()) .setTargetProtocolAddress(IP); Ethernet eth = new Ethernet(); eth.setEtherType(Ethernet.TYPE_ARP) .setVlanID(VLAN.toShort()) .setSourceMACAddress(MAC.toBytes()) .setDestinationMACAddress(BCMAC) .setPayload(arp); ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), portNumber(INPORT)); return new DefaultInboundPacket(receivedFrom, eth, ByteBuffer.wrap(eth.serialize())); }
private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) { ARP arpReply = new ARP(); arpReply .setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(ARP.PROTO_TYPE_IP) .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH) .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH) .setOpCode(ARP.OP_REPLY) .setSenderHardwareAddress(targetMac.toBytes()) .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress()) .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress()) .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress()); Ethernet eth = new Ethernet(); eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress()) .setSourceMACAddress(targetMac.toBytes()) .setEtherType(Ethernet.TYPE_ARP) .setPayload(arpReply); MacAddress hostMac = MacAddress.valueOf(arpReply.getTargetHardwareAddress()); HostId dstId = HostId.hostId(hostMac, vlanId); Host dst = srManager.hostService.getHost(dstId); if (dst == null) { log.warn("Cannot send ARP response to host {}", dstId); return; } TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(dst.location().port()).build(); OutboundPacket packet = new DefaultOutboundPacket( dst.location().deviceId(), treatment, ByteBuffer.wrap(eth.serialize())); srManager.packetService.emit(packet); }
/** * Processes incoming ARP packets. * * <p>If it is an ARP request to router itself or known hosts, then it sends ARP response. If it * is an ARP request to unknown hosts in its own subnet, then it flood the ARP request to the * ports. If it is an ARP response, then set a flow rule for the host and forward any IP packets * to the host in the packet buffer to the host. * * <p>Note: We handles all ARP packet in, even for those ARP packets between hosts in the same * subnet. For an ARP packet with broadcast destination MAC, some switches pipelines will send it * to the controller due to table miss, other swithches will flood the packets directly in the * data plane without packet in. We can deal with both cases. * * @param pkt incoming packet */ public void processPacketIn(InboundPacket pkt) { Ethernet ethernet = pkt.parsed(); ARP arp = (ARP) ethernet.getPayload(); ConnectPoint connectPoint = pkt.receivedFrom(); PortNumber inPort = connectPoint.port(); DeviceId deviceId = connectPoint.deviceId(); byte[] senderMacAddressByte = arp.getSenderHardwareAddress(); Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress()); srManager.routingRulePopulator.populateIpRuleForHost( deviceId, hostIpAddress, MacAddress.valueOf(senderMacAddressByte), inPort); if (arp.getOpCode() == ARP.OP_REQUEST) { handleArpRequest(deviceId, connectPoint, ethernet); } else { handleArpReply(deviceId, connectPoint, ethernet); } }
private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) { ARP arpRequest = (ARP) payload.getPayload(); VlanId vlanId = VlanId.vlanId(payload.getVlanID()); HostId targetHostId = HostId.hostId(MacAddress.valueOf(arpRequest.getTargetHardwareAddress()), vlanId); // ARP request for router. Send ARP reply. if (isArpForRouter(deviceId, arpRequest)) { Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress()); sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId); } else { Host targetHost = srManager.hostService.getHost(targetHostId); // ARP request for known hosts. Send proxy ARP reply on behalf of the target. if (targetHost != null) { removeVlanAndForward(payload, targetHost.location()); // ARP request for unknown host in the subnet. Flood in the subnet. } else { removeVlanAndFlood(payload, inPort); } } }
/** * Builds an ARP 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 ARP request we got * @return an Ethernet frame containing the ARP reply */ public static Ethernet buildArpReply(Ip4Address srcIp, MacAddress srcMac, Ethernet request) { Ethernet eth = new Ethernet(); eth.setDestinationMACAddress(request.getSourceMAC()); eth.setSourceMACAddress(srcMac); eth.setEtherType(Ethernet.TYPE_ARP); eth.setVlanID(request.getVlanID()); ARP arp = new ARP(); arp.setOpCode(ARP.OP_REPLY); arp.setProtocolType(ARP.PROTO_TYPE_IP); arp.setHardwareType(ARP.HW_TYPE_ETHERNET); arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH); arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); arp.setSenderHardwareAddress(srcMac.toBytes()); arp.setTargetHardwareAddress(request.getSourceMACAddress()); arp.setTargetProtocolAddress(((ARP) request.getPayload()).getSenderProtocolAddress()); arp.setSenderProtocolAddress(srcIp.toInt()); eth.setPayload(arp); return eth; }
private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) { Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpMsg.getTargetProtocolAddress()); Set<Ip4Address> gatewayIpAddresses = null; try { if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) { return true; } gatewayIpAddresses = config.getPortIPs(deviceId); } catch (DeviceConfigNotFoundException e) { log.warn(e.getMessage() + " Aborting check for router IP in processing arp"); } if (gatewayIpAddresses != null && gatewayIpAddresses.contains(targetProtocolAddress)) { return true; } return false; }
@Override public void process(PacketContext context) { if (context == null) { return; } Ethernet eth = context.inPacket().parsed(); if (eth == null) { return; } VlanId vlan = VlanId.vlanId(eth.getVlanID()); ConnectPoint heardOn = context.inPacket().receivedFrom(); // If this is not an edge port, bail out. Topology topology = topologyService.currentTopology(); if (topologyService.isInfrastructure(topology, heardOn)) { return; } HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis()); HostId hid = HostId.hostId(eth.getSourceMAC(), vlan); // ARP: possible new hosts, update both location and IP if (eth.getEtherType() == Ethernet.TYPE_ARP) { ARP arp = (ARP) eth.getPayload(); IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, arp.getSenderProtocolAddress()); HostDescription hdescr = new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip); providerService.hostDetected(hid, hdescr); // IPv4: update location only } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) { HostDescription hdescr = new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc); providerService.hostDetected(hid, hdescr); // NeighborAdvertisement and NeighborSolicitation: possible new hosts, update both location // and IP // IPv6: update location only } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) { IpAddress ip = null; IPv6 ipv6 = (IPv6) eth.getPayload(); IPacket iPkt = ipv6; while (iPkt != null) { if (iPkt instanceof NeighborAdvertisement || iPkt instanceof NeighborSolicitation) { IpAddress sourceAddress = IpAddress.valueOf(IpAddress.Version.INET6, ipv6.getSourceAddress()); // Ignore DAD packets, in which source address is all zeros. if (!sourceAddress.isZero()) { ip = sourceAddress; break; } } iPkt = iPkt.getPayload(); } HostDescription hdescr = (ip == null) ? new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc) : new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip); providerService.hostDetected(hid, hdescr); } }