X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=dhcpservice%2Fdhcpservice-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fvpnservice%2Fdhcpservice%2FDhcpPktHandler.java;fp=dhcpservice%2Fdhcpservice-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fvpnservice%2Fdhcpservice%2FDhcpPktHandler.java;h=3d10fa420b53edd9acf77a1233efc722c528194c;hb=2dace9ee1047a897bc6c0f3d1a8b05301797bc99;hp=16a2dc19d53302196e0e10dd96120db54f537371;hpb=0f032724da670d7ce19884bd41f7f839f71ad914;p=vpnservice.git diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java index 16a2dc19..3d10fa42 100644 --- a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java @@ -7,31 +7,515 @@ */ package org.opendaylight.vpnservice.dhcpservice; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.net.util.SubnetUtils; +import org.apache.commons.net.util.SubnetUtils.SubnetInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +import org.opendaylight.controller.liblldp.EtherTypes; +import org.opendaylight.controller.liblldp.HexEncode; +import org.opendaylight.controller.liblldp.NetUtils; +import org.opendaylight.controller.liblldp.PacketException; import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.dhcpservice.api.DHCP; +import org.opendaylight.vpnservice.dhcpservice.api.DHCPConstants; +import org.opendaylight.vpnservice.dhcpservice.api.DHCPMConstants; +import org.opendaylight.vpnservice.dhcpservice.api.DHCPUtils; +import org.opendaylight.vpnservice.mdsalutil.MDSALDataStoreUtils; +import org.opendaylight.vpnservice.mdsalutil.packet.Ethernet; +import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols; +import org.opendaylight.vpnservice.mdsalutil.packet.IPv4; +import org.opendaylight.vpnservice.mdsalutil.packet.UDP; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnet.attributes.HostRoutes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import com.google.common.base.Optional; public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class); private final DataBroker dataBroker; + private final DhcpManager dhcpMgr; + + private boolean computeUdpChecksum = true; + private PacketProcessingService pktService; - public DhcpPktHandler(final DataBroker broker) { + public DhcpPktHandler(final DataBroker broker, final DhcpManager dhcpManager) { this.dataBroker = broker; + dhcpMgr = dhcpManager; } + //TODO: Handle this in a separate thread @Override - public void onPacketReceived(PacketReceived pktReceived) { - LOG.info("Pkt received: {}",pktReceived); + public void onPacketReceived(PacketReceived packet) { + LOG.trace("Pkt received: {}", packet); + Class pktInReason = packet.getPacketInReason(); + short tableId = packet.getTableId().getValue(); + if (isPktInReasonSendtoCtrl(pktInReason) && ((DHCPMConstants.DHCP_TABLE == tableId))) { + byte[] inPayload = packet.getPayload(); + Ethernet ethPkt = new Ethernet(); + try { + ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte); + } catch (Exception e) { + LOG.warn("Failed to decode DHCP Packet", e); + return; + } + try { + DHCP pktIn = getDhcpPktIn(ethPkt); + LOG.trace("DHCPPkt received: {}", pktIn); + if (pktIn != null) { + NodeConnectorRef inNcRef = packet.getIngress(); + FlowCapableNodeConnector fcNc = this.getFlowCapableNodeConnector(inNcRef); + DHCP replyPkt = handleDhcpPacket(pktIn, fcNc); + byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, fcNc); + sendPacketOut(pktOut, inNcRef); + } + } catch (Exception e) { + LOG.warn("Failed to get DHCP Reply", e); + } + } + } + + private void sendPacketOut(byte[] pktOut, NodeConnectorRef ingress) { + // We go out the same port we came in on + InstanceIdentifier egressNodePath = getNodePath(ingress.getValue()); + TransmitPacketInput input = new TransmitPacketInputBuilder() + .setPayload(pktOut).setNode(new NodeRef(egressNodePath)) + .setEgress(ingress).build(); + LOG.trace("Transmitting packet: {}",input); + this.pktService.transmitPacket(input); + } + + private InstanceIdentifier getNodePath(InstanceIdentifier nodeInstanceId) { + return nodeInstanceId.firstIdentifierOf(Node.class); + } + + private DHCP handleDhcpPacket(DHCP dhcpPkt, FlowCapableNodeConnector fcNc) { + LOG.debug("DHCP pkt rcvd {}", dhcpPkt); + byte msgType = dhcpPkt.getMsgType(); + if (msgType == DHCPConstants.MSG_DECLINE) { + LOG.debug("DHCPDECLINE received"); + return null; + } else if (msgType == DHCPConstants.MSG_RELEASE) { + LOG.debug("DHCPRELEASE received"); + return null; + } + + Port nPort = getNeutronPort(fcNc); + Subnet nSubnet = getNeutronSubnet(nPort); + DhcpInfo dhcpInfo = getDhcpInfo(nPort, nSubnet); + LOG.trace("NeutronPort: {} \n NeutronSubnet: {}, dhcpInfo{}",nPort, nSubnet, dhcpInfo); + DHCP reply = null; + if (dhcpInfo != null) { + if (msgType == DHCPConstants.MSG_DISCOVER) { + reply = getReplyToDiscover(dhcpPkt, dhcpInfo); + } else if (msgType == DHCPConstants.MSG_REQUEST) { + reply = getReplyToRequest(dhcpPkt, dhcpInfo); + } + } + + return reply; + } + + private DhcpInfo getDhcpInfo(Port nPort, Subnet nSubnet) { + DhcpInfo dhcpInfo = null; + if( (nPort != null) && (nSubnet != null) ) { + String clientIp = nPort.getFixedIps().get(0).getIpAddress().getIpv4Address().getValue(); + String serverIp = nSubnet.getGatewayIp().getIpv4Address().getValue(); + List dnsServers = nSubnet.getDnsNameservers(); + dhcpInfo = new DhcpInfo(); + dhcpInfo.setClientIp(clientIp).setServerIp(serverIp) + .setCidr(nSubnet.getCidr()).setHostRoutes(nSubnet.getHostRoutes()) + .setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp); + } else { + //FIXME: Delete this test code + LOG.error("TestOnly Code"); + dhcpInfo = new DhcpInfo(); + dhcpInfo.setClientIp("1.1.1.3").setServerIp("1.1.1.1") + .setCidr("1.1.1.0/24").addDnsServer("1.1.1.1"); + LOG.warn("Failed to get Subnet info for DHCP reply"); + } + return dhcpInfo; + } + + private Subnet getNeutronSubnet(Port nPort) { + return dhcpMgr.getNeutronSubnet(nPort); + } + + private Port getNeutronPort(FlowCapableNodeConnector fcNc) { + return dhcpMgr.getNeutronPort(fcNc.getName()); + } + + private FlowCapableNodeConnector getFlowCapableNodeConnector(NodeConnectorRef inNcRef) { + InstanceIdentifier ncId = inNcRef.getValue().firstIdentifierOf(NodeConnector.class); + Optional nodeConnector = + MDSALDataStoreUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, ncId); + if(nodeConnector.isPresent()) { + NodeConnector nc = nodeConnector.get(); + LOG.trace("Incoming pkt's NodeConnector: {}", nc); + FlowCapableNodeConnector fcnc = nc.getAugmentation(FlowCapableNodeConnector.class); + return fcnc; + } + return null; + } + + private DHCP getDhcpPktIn(Ethernet ethPkt) { + if (ethPkt.getPayload() instanceof IPv4) { + IPv4 ipPkt = (IPv4) ethPkt.getPayload(); + if (ipPkt.getPayload() instanceof UDP) { + UDP udpPkt = (UDP) ipPkt.getPayload(); + if ((udpPkt.getSourcePort() == DHCPMConstants.dhcpClientPort) + && (udpPkt.getDestinationPort() == DHCPMConstants.dhcpServerPort)) { + byte[] rawDhcpPayload = udpPkt.getRawPayload(); + DHCP reply = new DHCP(); + try { + reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length); + } catch (PacketException e) { + LOG.warn("Failed to deserialize DHCP pkt", e); + return null; + } + return reply; + } + } + } + return null; + } + + DHCP getReplyToDiscover(DHCP dhcpPkt, DhcpInfo dhcpInfo) { + DHCP reply = new DHCP(); + reply.setOp(DHCPConstants.BOOTREPLY); + reply.setHtype(dhcpPkt.getHtype()); + reply.setHlen(dhcpPkt.getHlen()); + reply.setHops((byte) 0); + reply.setXid(dhcpPkt.getXid()); + reply.setSecs((short) 0); + + reply.setYiaddr(dhcpInfo.getClientIp()); + reply.setSiaddr(dhcpInfo.getServerIp()); + + reply.setFlags(dhcpPkt.getFlags()); + reply.setGiaddr(dhcpPkt.getGiaddr()); + reply.setChaddr(dhcpPkt.getChaddr()); + + reply.setMsgType(DHCPConstants.MSG_OFFER); + if(dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) { + setParameterListOptions(dhcpPkt, reply, dhcpInfo); + } + setCommonOptions(reply, dhcpInfo); + return reply; + } + + DHCP getReplyToRequest(DHCP dhcpPkt, DhcpInfo dhcpInfo) { + boolean sendAck = false; + byte[] requestedIp = null; + DHCP reply = new DHCP(); + reply.setOp(DHCPConstants.BOOTREPLY); + reply.setHtype(dhcpPkt.getHtype()); + reply.setHlen(dhcpPkt.getHlen()); + reply.setHops((byte) 0); + reply.setXid(dhcpPkt.getXid()); + reply.setSecs((short) 0); + + reply.setFlags(dhcpPkt.getFlags()); + reply.setGiaddr(dhcpPkt.getGiaddr()); + reply.setChaddr(dhcpPkt.getChaddr()); + byte[] allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp()); + if(Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) { + //This means a renew request + sendAck = true; + } else { + requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS); + sendAck = Arrays.equals(allocatedIp, requestedIp); + } + + if (sendAck) { + reply.setCiaddr(dhcpPkt.getCiaddr()); + reply.setYiaddr(dhcpInfo.getClientIp()); + reply.setSiaddr(dhcpInfo.getServerIp()); + reply.setMsgType(DHCPConstants.MSG_ACK); + if(dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) { + setParameterListOptions(dhcpPkt, reply, dhcpInfo); + } + setCommonOptions(reply, dhcpInfo); + } + else { + reply.setMsgType(DHCPConstants.MSG_NAK); + } + return reply; + } + + protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, FlowCapableNodeConnector fcNc) { + if (reply == null) { + /* + * DECLINE or RELEASE don't result in reply packet + */ + return null; + } + LOG.debug("Sending DHCP Pkt {}", reply); + // create UDP pkt + UDP udpPkt = new UDP(); + byte[] rawPkt; + try { + rawPkt = reply.serialize(); + } catch (PacketException e2) { + // TODO Auto-generated catch block + e2.printStackTrace(); + return null; + } + udpPkt.setRawPayload(rawPkt); + udpPkt.setDestinationPort(DHCPMConstants.dhcpClientPort); + udpPkt.setSourcePort(DHCPMConstants.dhcpServerPort); + udpPkt.setLength((short) (rawPkt.length + 8)); + //Create IP Pkt + IPv4 ip4Reply = new IPv4(); + try { + rawPkt = udpPkt.serialize(); + } catch (PacketException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + short checkSum = 0; + if(this.computeUdpChecksum) { + checkSum = computeChecksum(rawPkt, reply.getSiaddr(), + NetUtils.intToByteArray4(DHCPMConstants.BCAST_IP)); + } + udpPkt.setChecksum(checkSum); + ip4Reply.setPayload(udpPkt); + ip4Reply.setProtocol(IPProtocols.UDP.byteValue()); + ip4Reply.setSourceAddress(reply.getSiaddrAsInetAddr()); + ip4Reply.setDestinationAddress(DHCPMConstants.BCAST_IP); + ip4Reply.setTotalLength((short) (rawPkt.length+20)); + ip4Reply.setTtl((byte) 32); + // create Ethernet Frame + Ethernet ether = new Ethernet(); + //TODO: + ether.setSourceMACAddress(getServerMacAddress(fcNc)); + ether.setDestinationMACAddress(etherPkt.getSourceMACAddress()); + ether.setEtherType(EtherTypes.IPv4.shortValue()); + ether.setPayload(ip4Reply); + try { + rawPkt = ether.serialize(); + } catch (PacketException e) { + LOG.warn("Failed to serialize ethernet reply",e); + return null; + } + return rawPkt; + } + + private byte[] getServerMacAddress(FlowCapableNodeConnector fcNc) { + // Should we return ControllerMac instead? + MacAddress macAddress = fcNc.getHardwareAddress(); + return DHCPUtils.strMacAddrtoByteArray(macAddress.getValue()); + } + + public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) { + short checkSum = (short) 0; + int sum = 0, carry = 0; + int wordData, i; + + for (i = 0; i < inData.length - 1; i = i + 2) { + // Skip, if the current bytes are checkSum bytes + wordData = ((inData[i] << 8) & 0xFF00) + (inData[i + 1] & 0xFF); + sum = sum + wordData; + } + + if (i < inData.length) { + wordData = ((inData[i] << 8) & 0xFF00) + (0 & 0xFF); + sum = sum + wordData; + } + + for (i = 0; i < 4; i = i + 2) { + wordData = ((srcAddr[i] << 8) & 0xFF00) + (srcAddr[i + 1] & 0xFF); + sum = sum + wordData; + } + + for (i = 0; i < 4; i = i + 2) { + wordData = ((destAddr[i] << 8) & 0xFF00) + (destAddr[i + 1] & 0xFF); + sum = sum + wordData; + } + sum = sum + 17 + inData.length; + + while((sum >> 16) != 0) { + carry = (sum >> 16); + sum = (sum & 0xFFFF)+ carry; + } + checkSum = (short) ~((short) sum & 0xFFFF); + if(checkSum == 0) { + checkSum = (short)0xffff; + } + return checkSum; + } + + private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) { + pkt.setOptionInt(DHCPConstants.OPT_LEASE_TIME, dhcpMgr.getDhcpLeaseTime()); + if (dhcpMgr.getDhcpDefDomain() != null) { + pkt.setOptionString(DHCPConstants.OPT_DOMAIN_NAME, dhcpMgr.getDhcpDefDomain()); + } + if(dhcpMgr.getDhcpLeaseTime() > 0) { + pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime()); + pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime()); + } + SubnetUtils util = null; + SubnetInfo info = null; + util = new SubnetUtils(dhcpInfo.getCidr()); + info = util.getInfo(); + String gwIp = dhcpInfo.getGatewayIp(); + List dnServers = dhcpInfo.getDnsServers(); + try { + /* + * setParameterListOptions may have initialized some of these + * options to maintain order. If we can't fill them, unset to avoid + * sending wrong information in reply. + */ + if (gwIp != null) { + pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, gwIp); + pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp); + } else { + pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER); + pkt.unsetOption(DHCPConstants.OPT_ROUTERS); + } + if (info != null) { + pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask()); + pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress()); + } else { + pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK); + pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS); + } + if ((dnServers != null) && (dnServers.size() > 0)) { + pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers); + } else { + pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS); + } + } catch (UnknownHostException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void setParameterListOptions(DHCP req, DHCP reply, DhcpInfo dhcpInfo) { + byte[] paramList = req.getOptionBytes(DHCPConstants.OPT_PARAMETER_REQUEST_LIST); + for(int i = 0; i < paramList.length; i++) { + switch (paramList[i]) { + case DHCPConstants.OPT_SUBNET_MASK: + case DHCPConstants.OPT_ROUTERS: + case DHCPConstants.OPT_SERVER_IDENTIFIER: + case DHCPConstants.OPT_DOMAIN_NAME_SERVERS: + case DHCPConstants.OPT_BROADCAST_ADDRESS: + case DHCPConstants.OPT_LEASE_TIME: + case DHCPConstants.OPT_RENEWAL_TIME: + case DHCPConstants.OPT_REBINDING_TIME: + /* These values will be filled in setCommonOptions + * Setting these just to preserve order as + * specified in PARAMETER_REQUEST_LIST. + */ + reply.setOptionInt(paramList[i], 0); + break; + case DHCPConstants.OPT_DOMAIN_NAME: + reply.setOptionString(paramList[i], " "); + break; + case DHCPConstants.OPT_CLASSLESS_ROUTE: + setOptionClasslessRoute(reply, dhcpInfo); + break; + default: + LOG.debug("DHCP Option code {} not supported yet", paramList[i]); + break; + } + } + } + private void setOptionClasslessRoute(DHCP reply, DhcpInfo dhcpInfo) { + List hostRoutes = dhcpInfo.getHostRoutes(); + if(hostRoutes == null) { + //we can't set this option, so return + return; + } + ByteArrayOutputStream result = new ByteArrayOutputStream(); + Iterator iter = hostRoutes.iterator(); + while(iter.hasNext()) { + HostRoutes hostRoute = iter.next(); + String router = hostRoute.getNexthop().toString(); + String dest = hostRoute.getDestination().toString(); + try { + result.write(convertToClasslessRouteOption(dest, router)); + } catch (IOException | NullPointerException e) { + LOG.debug("Exception {}",e.getMessage()); + } + } + if (result.size() > 0) { + reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray()); + } + } + + protected byte[] convertToClasslessRouteOption(String dest, String router) { + ByteArrayOutputStream bArr = new ByteArrayOutputStream(); + if((dest == null || + router == null)) { + return null; + } + + //get prefix + Short prefix = null; + String[] parts = dest.split("/"); + if (parts.length < 2) { + prefix = new Short((short)0); + } else { + prefix = Short.valueOf(parts[1]); + } + + bArr.write(prefix.byteValue()); + SubnetUtils util = new SubnetUtils(dest); + SubnetInfo info = util.getInfo(); + String strNetAddr = info.getNetworkAddress(); + try { + byte[] netAddr = InetAddress.getByName(strNetAddr).getAddress(); + //Strip any trailing 0s from netAddr + for(int i = 0; i < netAddr.length;i++) { + if(netAddr[i] != 0) { + bArr.write(netAddr,i,1); + } + } + bArr.write(InetAddress.getByName(router).getAddress()); + } catch (IOException e) { + return null; + } + return bArr.toByteArray(); + } + + private boolean isPktInReasonSendtoCtrl(Class pktInReason) { + return (pktInReason == SendToController.class); } @Override public void close() throws Exception { // TODO Auto-generated method stub - + } + + public void setPacketProcessingService(PacketProcessingService packetService) { + this.pktService = packetService; } }