private void sendPacketOut(ConnectPoint outport, Ethernet payload, int sid) { IPv4 ipPacket = (IPv4) payload.getPayload(); Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress()); if (sid == -1 || config.getSegmentId(payload.getDestinationMAC()) == sid || config.inSameSubnet(outport.deviceId(), destIpAddress)) { TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(outport.port()).build(); OutboundPacket packet = new DefaultOutboundPacket( outport.deviceId(), treatment, ByteBuffer.wrap(payload.serialize())); srManager.packetService.emit(packet); } else { log.info("Send a MPLS packet as a ICMP response"); TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(outport.port()).build(); payload.setEtherType(Ethernet.MPLS_UNICAST); MPLS mplsPkt = new MPLS(); mplsPkt.setLabel(sid); mplsPkt.setTtl(((IPv4) payload.getPayload()).getTtl()); mplsPkt.setPayload(payload.getPayload()); payload.setPayload(mplsPkt); OutboundPacket packet = new DefaultOutboundPacket( outport.deviceId(), treatment, ByteBuffer.wrap(payload.serialize())); srManager.packetService.emit(packet); } }
/** * 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 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; }
/** * Process incoming ICMP packet. If it is an ICMP request to router or known host, then sends an * ICMP response. If it is an ICMP packet to known host and forward the packet to the host. If it * is an ICMP packet to unknown host in a subnet, then sends an ARP request to the subnet. * * @param pkt inbound packet */ public void processPacketIn(InboundPacket pkt) { Ethernet ethernet = pkt.parsed(); IPv4 ipv4 = (IPv4) ethernet.getPayload(); ConnectPoint connectPoint = pkt.receivedFrom(); DeviceId deviceId = connectPoint.deviceId(); Ip4Address destinationAddress = Ip4Address.valueOf(ipv4.getDestinationAddress()); Set<Ip4Address> gatewayIpAddresses = config.getPortIPs(deviceId); Ip4Address routerIp; try { routerIp = config.getRouterIp(deviceId); } catch (DeviceConfigNotFoundException e) { log.warn(e.getMessage() + " Aborting processPacketIn."); return; } IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH); Ip4Address routerIpAddress = routerIpPrefix.getIp4Prefix().address(); // ICMP to the router IP or gateway IP if (((ICMP) ipv4.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST && (destinationAddress.equals(routerIpAddress) || gatewayIpAddresses.contains(destinationAddress))) { sendICMPResponse(ethernet, connectPoint); // ICMP for any known host } else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) { // TODO: known host packet should not be coming to controller - resend flows? srManager.ipHandler.forwardPackets(deviceId, destinationAddress); // ICMP for an unknown host in the subnet of the router } else if (config.inSameSubnet(deviceId, destinationAddress)) { srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint); // ICMP for an unknown host } else { log.debug("ICMP request for unknown host {} ", destinationAddress); // Do nothing } }
/** * Sends an ICMP reply message. * * <p>Note: we assume that packets sending from the edge switches to the hosts have untagged VLAN. * * @param icmpRequest the original ICMP request * @param outport the output port where the ICMP reply should be sent to */ private void sendICMPResponse(Ethernet icmpRequest, ConnectPoint outport) { // Note: We assume that packets arrive at the edge switches have // untagged VLAN. Ethernet icmpReplyEth = new Ethernet(); IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload(); IPv4 icmpReplyIpv4 = new IPv4(); int destAddress = icmpRequestIpv4.getDestinationAddress(); icmpReplyIpv4.setDestinationAddress(icmpRequestIpv4.getSourceAddress()); icmpReplyIpv4.setSourceAddress(destAddress); icmpReplyIpv4.setTtl((byte) 64); icmpReplyIpv4.setChecksum((short) 0); ICMP icmpReply = new ICMP(); icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload()); icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY); icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY); icmpReply.setChecksum((short) 0); icmpReplyIpv4.setPayload(icmpReply); icmpReplyEth.setPayload(icmpReplyIpv4); icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4); icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress()); icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress()); Ip4Address destIpAddress = Ip4Address.valueOf(icmpReplyIpv4.getDestinationAddress()); Ip4Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress); int sid = config.getSegmentId(destRouterAddress); if (sid < 0) { log.warn("Cannot find the Segment ID for {}", destAddress); return; } sendPacketOut(outport, icmpReplyEth, sid); }
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); } } }