[CORD-1614] Refactor DHCP relay app
Change-Id: Id4a281526aa5469abe5732e5d2d42d34074907e9
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
new file mode 100644
index 0000000..9abe49e
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright 2017-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.dhcprelay;
+
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.DHCP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.UDP;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.dhcp.CircuitId;
+import org.onlab.packet.dhcp.DhcpOption;
+import org.onlab.packet.dhcp.DhcpRelayAgentOption;
+import org.onosproject.dhcprelay.api.DhcpHandler;
+import org.onosproject.dhcprelay.store.DhcpRecord;
+import org.onosproject.dhcprelay.store.DhcpRelayStore;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.incubator.net.routing.Route;
+import org.onosproject.incubator.net.routing.RouteStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.host.HostStore;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
+import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
+import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
+import static org.onlab.packet.MacAddress.valueOf;
+import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID;
+
+@Component
+@Service
+@Property(name = "version", value = "4")
+public class Dhcp4HandlerImpl implements DhcpHandler {
+ private static Logger log = LoggerFactory.getLogger(Dhcp4HandlerImpl.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DhcpRelayStore dhcpRelayStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostStore hostStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected RouteStore routeStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private Ip4Address dhcpServerIp = null;
+ // dhcp server may be connected directly to the SDN network or
+ // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
+ // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
+ // to the gateway.
+ private ConnectPoint dhcpServerConnectPoint = null;
+ private MacAddress dhcpConnectMac = null;
+ private VlanId dhcpConnectVlan = null;
+ private Ip4Address dhcpGatewayIp = null;
+
+ @Override
+ public void setDhcpServerIp(IpAddress dhcpServerIp) {
+ checkNotNull(dhcpServerIp, "DHCP server IP can't be null");
+ checkState(dhcpServerIp.isIp4(), "Invalid server IP for DHCPv4 relay handler");
+ this.dhcpServerIp = dhcpServerIp.getIp4Address();
+ }
+
+ @Override
+ public void setDhcpServerConnectPoint(ConnectPoint dhcpServerConnectPoint) {
+ checkNotNull(dhcpServerConnectPoint, "Server connect point can't null");
+ this.dhcpServerConnectPoint = dhcpServerConnectPoint;
+ }
+
+ @Override
+ public void setDhcpConnectMac(MacAddress dhcpConnectMac) {
+ this.dhcpConnectMac = dhcpConnectMac;
+ }
+
+ @Override
+ public void setDhcpConnectVlan(VlanId dhcpConnectVlan) {
+ this.dhcpConnectVlan = dhcpConnectVlan;
+ }
+
+ @Override
+ public void setDhcpGatewayIp(IpAddress dhcpGatewayIp) {
+ if (dhcpGatewayIp != null) {
+ checkState(dhcpGatewayIp.isIp4(), "Invalid gateway IP for DHCPv4 relay handler");
+ this.dhcpGatewayIp = dhcpGatewayIp.getIp4Address();
+ } else {
+ // removes gateway config
+ this.dhcpGatewayIp = null;
+ }
+ }
+
+ @Override
+ public Optional<IpAddress> getDhcpServerIp() {
+ return Optional.ofNullable(dhcpServerIp);
+ }
+
+ @Override
+ public Optional<IpAddress> getDhcpGatewayIp() {
+ return Optional.ofNullable(dhcpGatewayIp);
+ }
+
+ @Override
+ public Optional<MacAddress> getDhcpConnectMac() {
+ return Optional.ofNullable(dhcpConnectMac);
+ }
+
+ @Override
+ public void processDhcpPacket(PacketContext context, BasePacket payload) {
+ checkNotNull(payload, "DHCP payload can't be null");
+ checkState(payload instanceof DHCP, "Payload is not a DHCP");
+ DHCP dhcpPayload = (DHCP) payload;
+ if (!configured()) {
+ log.warn("Missing DHCP relay server config. Abort packet processing");
+ return;
+ }
+
+ ConnectPoint inPort = context.inPacket().receivedFrom();
+ Set<Interface> clientServerInterfaces = interfaceService.getInterfacesByPort(inPort);
+ // ignore the packets if dhcp client interface is not configured on onos.
+ if (clientServerInterfaces.isEmpty()) {
+ log.warn("Virtual interface is not configured on {}", inPort);
+ return;
+ }
+ checkNotNull(dhcpPayload, "Can't find DHCP payload");
+ Ethernet packet = context.inPacket().parsed();
+ DHCP.MsgType incomingPacketType = dhcpPayload.getOptions().stream()
+ .filter(dhcpOption -> dhcpOption.getCode() == OptionCode_MessageType.getValue())
+ .map(DhcpOption::getData)
+ .map(data -> DHCP.MsgType.getType(data[0]))
+ .findFirst()
+ .orElse(null);
+ checkNotNull(incomingPacketType, "Can't get message type from DHCP payload {}", dhcpPayload);
+ switch (incomingPacketType) {
+ case DHCPDISCOVER:
+ // add the gatewayip as virtual interface ip for server to understand
+ // the lease to be assigned and forward the packet to dhcp server.
+ Ethernet ethernetPacketDiscover =
+ processDhcpPacketFromClient(context, packet, clientServerInterfaces);
+
+ if (ethernetPacketDiscover != null) {
+ writeRequestDhcpRecord(inPort, packet, dhcpPayload);
+ handleDhcpDiscoverAndRequest(ethernetPacketDiscover);
+ }
+ break;
+ case DHCPOFFER:
+ //reply to dhcp client.
+ Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
+ if (ethernetPacketOffer != null) {
+ writeResponseDhcpRecord(ethernetPacketOffer, dhcpPayload);
+ handleDhcpOffer(ethernetPacketOffer, dhcpPayload);
+ }
+ break;
+ case DHCPREQUEST:
+ // add the gateway ip as virtual interface ip for server to understand
+ // the lease to be assigned and forward the packet to dhcp server.
+ Ethernet ethernetPacketRequest =
+ processDhcpPacketFromClient(context, packet, clientServerInterfaces);
+ if (ethernetPacketRequest != null) {
+ writeRequestDhcpRecord(inPort, packet, dhcpPayload);
+ handleDhcpDiscoverAndRequest(ethernetPacketRequest);
+ }
+ break;
+ case DHCPACK:
+ // reply to dhcp client.
+ Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
+ if (ethernetPacketAck != null) {
+ writeResponseDhcpRecord(ethernetPacketAck, dhcpPayload);
+ handleDhcpAck(ethernetPacketAck, dhcpPayload);
+ }
+ break;
+ case DHCPRELEASE:
+ // TODO: release the ip address from client
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Checks if this app has been configured.
+ *
+ * @return true if all information we need have been initialized
+ */
+ public boolean configured() {
+ return dhcpServerConnectPoint != null && dhcpServerIp != null;
+ }
+
+ /**
+ * Returns the first interface ip out of a set of interfaces or null.
+ *
+ * @param intfs interfaces of one connect port
+ * @return the first interface IP; null if not exists an IP address in
+ * these interfaces
+ */
+ private Ip4Address getRelayAgentIPv4Address(Set<Interface> intfs) {
+ return intfs.stream()
+ .map(Interface::ipAddressesList)
+ .flatMap(List::stream)
+ .map(InterfaceIpAddress::ipAddress)
+ .filter(IpAddress::isIp4)
+ .map(IpAddress::getIp4Address)
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * Build the DHCP discover/request packet with gateway IP(unicast packet).
+ *
+ * @param context the packet context
+ * @param ethernetPacket the ethernet payload to process
+ * @param clientInterfaces interfaces which belongs to input port
+ * @return processed packet
+ */
+ private Ethernet processDhcpPacketFromClient(PacketContext context,
+ Ethernet ethernetPacket,
+ Set<Interface> clientInterfaces) {
+ Ip4Address relayAgentIp = getRelayAgentIPv4Address(clientInterfaces);
+ MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
+ if (relayAgentIp == null || relayAgentMac == null) {
+ log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
+ + "packet from client on port: {}. Aborting packet processing",
+ clientInterfaces.iterator().next().connectPoint());
+ return null;
+ }
+ if (dhcpConnectMac == null) {
+ log.warn("DHCP {} not yet resolved .. Aborting DHCP "
+ + "packet processing from client on port: {}",
+ (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
+ : "gateway IP " + dhcpGatewayIp,
+ clientInterfaces.iterator().next().connectPoint());
+ return null;
+ }
+ // get dhcp header.
+ Ethernet etherReply = (Ethernet) ethernetPacket.clone();
+ etherReply.setSourceMACAddress(relayAgentMac);
+ etherReply.setDestinationMACAddress(dhcpConnectMac);
+ etherReply.setVlanID(dhcpConnectVlan.toShort());
+ IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
+ ipv4Packet.setSourceAddress(relayAgentIp.toInt());
+ ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
+ UDP udpPacket = (UDP) ipv4Packet.getPayload();
+ DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
+
+ // If there is no relay agent option(option 82), add one to DHCP payload
+ boolean containsRelayAgentOption = dhcpPacket.getOptions().stream()
+ .map(DhcpOption::getCode)
+ .anyMatch(code -> code == OptionCode_CircuitID.getValue());
+
+ if (!containsRelayAgentOption) {
+ ConnectPoint inPort = context.inPacket().receivedFrom();
+ VlanId vlanId = VlanId.vlanId(ethernetPacket.getVlanID());
+ // add connected in port and vlan
+ CircuitId cid = new CircuitId(inPort.toString(), vlanId);
+ byte[] circuitId = cid.serialize();
+ DhcpOption circuitIdSubOpt = new DhcpOption();
+ circuitIdSubOpt
+ .setCode(CIRCUIT_ID.getValue())
+ .setLength((byte) circuitId.length)
+ .setData(circuitId);
+
+ DhcpRelayAgentOption newRelayAgentOpt = new DhcpRelayAgentOption();
+ newRelayAgentOpt.setCode(OptionCode_CircuitID.getValue());
+ newRelayAgentOpt.addSubOption(circuitIdSubOpt);
+
+ // Removes END option first
+ List<DhcpOption> options = dhcpPacket.getOptions().stream()
+ .filter(opt -> opt.getCode() != OptionCode_END.getValue())
+ .collect(Collectors.toList());
+
+ // push relay agent option
+ options.add(newRelayAgentOpt);
+
+ // make sure option 255(End) is the last option
+ DhcpOption endOption = new DhcpOption();
+ endOption.setCode(OptionCode_END.getValue());
+ options.add(endOption);
+
+ dhcpPacket.setOptions(options);
+ dhcpPacket.setGatewayIPAddress(relayAgentIp.toInt());
+ }
+
+ udpPacket.setPayload(dhcpPacket);
+ udpPacket.setSourcePort(UDP.DHCP_CLIENT_PORT);
+ udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
+ ipv4Packet.setPayload(udpPacket);
+ etherReply.setPayload(ipv4Packet);
+ return etherReply;
+ }
+
+ /**
+ * Writes DHCP record to the store according to the request DHCP packet (Discover, Request).
+ *
+ * @param location the location which DHCP packet comes from
+ * @param ethernet the DHCP packet
+ * @param dhcpPayload the DHCP payload
+ */
+ private void writeRequestDhcpRecord(ConnectPoint location,
+ Ethernet ethernet,
+ DHCP dhcpPayload) {
+ VlanId vlanId = VlanId.vlanId(ethernet.getVlanID());
+ MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
+ HostId hostId = HostId.hostId(macAddress, vlanId);
+ DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
+ if (record == null) {
+ record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
+ } else {
+ record = record.clone();
+ }
+ record.addLocation(new HostLocation(location, System.currentTimeMillis()));
+ record.ip4Status(dhcpPayload.getPacketType());
+ record.setDirectlyConnected(directlyConnected(dhcpPayload));
+ if (!directlyConnected(dhcpPayload)) {
+ // Update gateway mac address if the host is not directly connected
+ record.nextHop(ethernet.getSourceMAC());
+ }
+ record.updateLastSeen();
+ dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
+ }
+
+ /**
+ * Writes DHCP record to the store according to the response DHCP packet (Offer, Ack).
+ *
+ * @param ethernet the DHCP packet
+ * @param dhcpPayload the DHCP payload
+ */
+ private void writeResponseDhcpRecord(Ethernet ethernet,
+ DHCP dhcpPayload) {
+ Optional<Interface> outInterface = getOutputInterface(ethernet, dhcpPayload);
+ if (!outInterface.isPresent()) {
+ log.warn("Failed to determine where to send {}", dhcpPayload.getPacketType());
+ return;
+ }
+
+ Interface outIface = outInterface.get();
+ ConnectPoint location = outIface.connectPoint();
+ VlanId vlanId = outIface.vlan();
+ MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
+ HostId hostId = HostId.hostId(macAddress, vlanId);
+ DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
+ if (record == null) {
+ record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
+ } else {
+ record = record.clone();
+ }
+ record.addLocation(new HostLocation(location, System.currentTimeMillis()));
+ if (dhcpPayload.getPacketType() == DHCP.MsgType.DHCPACK) {
+ record.ip4Address(Ip4Address.valueOf(dhcpPayload.getYourIPAddress()));
+ }
+ record.ip4Status(dhcpPayload.getPacketType());
+ record.setDirectlyConnected(directlyConnected(dhcpPayload));
+ record.updateLastSeen();
+ dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
+ }
+
+ /**
+ * Build the DHCP offer/ack with proper client port.
+ *
+ * @param ethernetPacket the original packet comes from server
+ * @return new packet which will send to the client
+ */
+ private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
+ // get dhcp header.
+ Ethernet etherReply = (Ethernet) ethernetPacket.clone();
+ IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
+ UDP udpPacket = (UDP) ipv4Packet.getPayload();
+ DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
+
+ // determine the vlanId of the client host - note that this vlan id
+ // could be different from the vlan in the packet from the server
+ Interface outInterface = getOutputInterface(ethernetPacket, dhcpPayload).orElse(null);
+
+ if (outInterface == null) {
+ log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
+ return null;
+ }
+
+ etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
+ etherReply.setVlanID(outInterface.vlan().toShort());
+ // we leave the srcMac from the original packet
+
+ // figure out the relay agent IP corresponding to the original request
+ Ip4Address relayAgentIP = getRelayAgentIPv4Address(
+ interfaceService.getInterfacesByPort(outInterface.connectPoint()));
+ if (relayAgentIP == null) {
+ log.warn("Cannot determine relay agent interface Ipv4 addr for host {}/{}. "
+ + "Aborting relay for dhcp packet from server {}",
+ etherReply.getDestinationMAC(), outInterface.vlan(),
+ ethernetPacket);
+ return null;
+ }
+ // SRC_IP: relay agent IP
+ // DST_IP: offered IP
+ ipv4Packet.setSourceAddress(relayAgentIP.toInt());
+ ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
+ udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
+ if (directlyConnected(dhcpPayload)) {
+ udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
+ } else {
+ // forward to another dhcp relay
+ udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
+ }
+
+ udpPacket.setPayload(dhcpPayload);
+ ipv4Packet.setPayload(udpPacket);
+ etherReply.setPayload(ipv4Packet);
+ return etherReply;
+ }
+
+
+ /**
+ * Check if the host is directly connected to the network or not.
+ *
+ * @param dhcpPayload the dhcp payload
+ * @return true if the host is directly connected to the network; false otherwise
+ */
+ private boolean directlyConnected(DHCP dhcpPayload) {
+ DhcpOption relayAgentOption = dhcpPayload.getOption(OptionCode_CircuitID);
+
+ // Doesn't contains relay option
+ if (relayAgentOption == null) {
+ return true;
+ }
+
+ IpAddress gatewayIp = IpAddress.valueOf(dhcpPayload.getGatewayIPAddress());
+ Set<Interface> gatewayInterfaces = interfaceService.getInterfacesByIp(gatewayIp);
+
+ // Contains relay option, and added by ONOS
+ if (!gatewayInterfaces.isEmpty()) {
+ return true;
+ }
+
+ // Relay option added by other relay agent
+ return false;
+ }
+
+
+ /**
+ * Send the DHCP ack to the requester host.
+ * Modify Host or Route store according to the type of DHCP.
+ *
+ * @param ethernetPacketAck the packet
+ * @param dhcpPayload the DHCP data
+ */
+ private void handleDhcpAck(Ethernet ethernetPacketAck, DHCP dhcpPayload) {
+ Optional<Interface> outInterface = getOutputInterface(ethernetPacketAck, dhcpPayload);
+ if (!outInterface.isPresent()) {
+ log.warn("Can't find output interface for dhcp: {}", dhcpPayload);
+ return;
+ }
+
+ Interface outIface = outInterface.get();
+ HostLocation hostLocation = new HostLocation(outIface.connectPoint(), System.currentTimeMillis());
+ MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
+ VlanId vlanId = outIface.vlan();
+ HostId hostId = HostId.hostId(macAddress, vlanId);
+ Ip4Address ip = Ip4Address.valueOf(dhcpPayload.getYourIPAddress());
+
+ if (directlyConnected(dhcpPayload)) {
+ // Add to host store if it connect to network directly
+ Set<IpAddress> ips = Sets.newHashSet(ip);
+ HostDescription desc = new DefaultHostDescription(macAddress, vlanId,
+ hostLocation, ips);
+
+ // Replace the ip when dhcp server give the host new ip address
+ hostStore.createOrUpdateHost(DhcpRelayManager.PROVIDER_ID, hostId, desc, false);
+ } else {
+ // Add to route store if it does not connect to network directly
+ // Get gateway host IP according to host mac address
+ DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
+
+ if (record == null) {
+ log.warn("Can't find DHCP record of host {}", hostId);
+ return;
+ }
+
+ MacAddress gwMac = record.nextHop().orElse(null);
+ if (gwMac == null) {
+ log.warn("Can't find gateway mac address from record {}", record);
+ return;
+ }
+
+ HostId gwHostId = HostId.hostId(gwMac, record.vlanId());
+ Host gwHost = hostService.getHost(gwHostId);
+
+ if (gwHost == null) {
+ log.warn("Can't find gateway host {}", gwHostId);
+ return;
+ }
+
+ Ip4Address nextHopIp = gwHost.ipAddresses()
+ .stream()
+ .filter(IpAddress::isIp4)
+ .map(IpAddress::getIp4Address)
+ .findFirst()
+ .orElse(null);
+
+ if (nextHopIp == null) {
+ log.warn("Can't find IP address of gateway {}", gwHost);
+ return;
+ }
+
+ Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
+ routeStore.updateRoute(route);
+ }
+ sendResponseToClient(ethernetPacketAck, dhcpPayload);
+ }
+
+ /**
+ * forward the packet to ConnectPoint where the DHCP server is attached.
+ *
+ * @param packet the packet
+ */
+ private void handleDhcpDiscoverAndRequest(Ethernet packet) {
+ // send packet to dhcp server connect point.
+ if (dhcpServerConnectPoint != null) {
+ TrafficTreatment t = DefaultTrafficTreatment.builder()
+ .setOutput(dhcpServerConnectPoint.port()).build();
+ OutboundPacket o = new DefaultOutboundPacket(
+ dhcpServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
+ if (log.isTraceEnabled()) {
+ log.trace("Relaying packet to dhcp server {}", packet);
+ }
+ packetService.emit(o);
+ } else {
+ log.warn("Can't find DHCP server connect point, abort.");
+ }
+ }
+
+
+ /**
+ * Gets output interface of a dhcp packet.
+ * If option 82 exists in the dhcp packet and the option was sent by
+ * ONOS (gateway address exists in ONOS interfaces), use the connect
+ * point and vlan id from circuit id; otherwise, find host by destination
+ * address and use vlan id from sender (dhcp server).
+ *
+ * @param ethPacket the ethernet packet
+ * @param dhcpPayload the dhcp packet
+ * @return an interface represent the output port and vlan; empty value
+ * if the host or circuit id not found
+ */
+ private Optional<Interface> getOutputInterface(Ethernet ethPacket, DHCP dhcpPayload) {
+ VlanId originalPacketVlanId = VlanId.vlanId(ethPacket.getVlanID());
+ IpAddress gatewayIpAddress = Ip4Address.valueOf(dhcpPayload.getGatewayIPAddress());
+ Set<Interface> gatewayInterfaces = interfaceService.getInterfacesByIp(gatewayIpAddress);
+ DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
+
+ // Sent by ONOS, and contains circuit id
+ if (!gatewayInterfaces.isEmpty() && option != null) {
+ DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
+ try {
+ CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
+ ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(circuitId.connectPoint());
+ VlanId vlanId = circuitId.vlanId();
+ return Optional.of(new Interface(null, connectPoint, null, null, vlanId));
+ } catch (IllegalArgumentException ex) {
+ // invalid circuit format, didn't sent by ONOS
+ log.debug("Invalid circuit {}, use information from dhcp payload",
+ circuitIdSubOption.getData());
+ }
+ }
+
+ // Use Vlan Id from DHCP server if DHCP relay circuit id was not
+ // sent by ONOS or circuit Id can't be parsed
+ MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
+ Optional<DhcpRecord> dhcpRecord = dhcpRelayStore.getDhcpRecord(HostId.hostId(dstMac, originalPacketVlanId));
+ return dhcpRecord
+ .map(DhcpRecord::locations)
+ .orElse(Collections.emptySet())
+ .stream()
+ .reduce((hl1, hl2) -> {
+ if (hl1 == null || hl2 == null) {
+ return hl1 == null ? hl2 : hl1;
+ }
+ return hl1.time() > hl2.time() ? hl1 : hl2;
+ })
+ .map(lastLocation -> new Interface(null, lastLocation, null, null, originalPacketVlanId));
+ }
+
+ /**
+ * Handles DHCP offer packet.
+ *
+ * @param ethPacket the packet
+ * @param dhcpPayload the DHCP data
+ */
+ private void handleDhcpOffer(Ethernet ethPacket, DHCP dhcpPayload) {
+ // TODO: removes option 82 if necessary
+ sendResponseToClient(ethPacket, dhcpPayload);
+ }
+
+ /**
+ * Send the response DHCP to the requester host.
+ *
+ * @param ethPacket the packet
+ * @param dhcpPayload the DHCP data
+ */
+ private void sendResponseToClient(Ethernet ethPacket, DHCP dhcpPayload) {
+ Optional<Interface> outInterface = getOutputInterface(ethPacket, dhcpPayload);
+ outInterface.ifPresent(theInterface -> {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(theInterface.connectPoint().port())
+ .build();
+ OutboundPacket o = new DefaultOutboundPacket(
+ theInterface.connectPoint().deviceId(),
+ treatment,
+ ByteBuffer.wrap(ethPacket.serialize()));
+ if (log.isTraceEnabled()) {
+ log.trace("Relaying packet to DHCP client {} via {}, vlan {}",
+ ethPacket,
+ theInterface.connectPoint(),
+ theInterface.vlan());
+ }
+ packetService.emit(o);
+ });
+ }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
new file mode 100644
index 0000000..f5375bf
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017-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.dhcprelay;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.dhcprelay.api.DhcpHandler;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.packet.PacketContext;
+
+import java.util.Optional;
+
+@Component
+@Service
+@Property(name = "version", value = "6")
+public class Dhcp6HandlerImpl implements DhcpHandler {
+
+ @Override
+ public void processDhcpPacket(PacketContext context, BasePacket dhcp6Payload) {
+
+ }
+
+ @Override
+ public Optional<IpAddress> getDhcpServerIp() {
+ return null;
+ }
+
+ @Override
+ public Optional<IpAddress> getDhcpGatewayIp() {
+ return null;
+ }
+
+ @Override
+ public Optional<MacAddress> getDhcpConnectMac() {
+ return null;
+ }
+
+ @Override
+ public void setDhcpGatewayIp(IpAddress dhcpGatewayIp) {
+
+ }
+
+ @Override
+ public void setDhcpConnectVlan(VlanId dhcpConnectVlan) {
+
+ }
+
+ @Override
+ public void setDhcpConnectMac(MacAddress dhcpConnectMac) {
+
+ }
+
+ @Override
+ public void setDhcpServerConnectPoint(ConnectPoint dhcpServerConnectPoint) {
+
+ }
+
+ @Override
+ public void setDhcpServerIp(IpAddress dhcpServerIp) {
+
+ }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
index 11a5d7a..0ed8411 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
@@ -33,7 +33,6 @@
@Override
public boolean isValid() {
-
return hasOnlyFields(DHCP_CONNECT_POINT, DHCP_SERVER_IP, DHCP_GATEWAY_IP) &&
isConnectPoint(DHCP_CONNECT_POINT, MANDATORY) &&
isIpAddress(DHCP_SERVER_IP, MANDATORY) &&
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
index de682de..a310a96 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
@@ -17,15 +17,12 @@
import java.nio.ByteBuffer;
import java.util.Collection;
-import java.util.Collections;
import java.util.Dictionary;
-import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
-import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -39,31 +36,26 @@
import org.onlab.packet.DHCP6;
import org.onlab.packet.IPacket;
import org.onlab.packet.IPv6;
-import org.onlab.packet.dhcp.DhcpOption;
-import org.onlab.packet.dhcp.CircuitId;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.TpPort;
import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
-import org.onlab.packet.dhcp.DhcpRelayAgentOption;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
+import org.onosproject.dhcprelay.api.DhcpHandler;
+import org.onosproject.dhcprelay.api.DhcpRelayService;
import org.onosproject.dhcprelay.store.DhcpRecord;
import org.onosproject.dhcprelay.store.DhcpRelayStore;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.incubator.net.routing.Route;
-import org.onosproject.incubator.net.routing.RouteStore;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
@@ -72,13 +64,9 @@
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.DefaultHostDescription;
-import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.HostStore;
-import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketContext;
@@ -92,12 +80,7 @@
import com.google.common.collect.ImmutableSet;
-import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
-import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
-import static org.onlab.packet.MacAddress.valueOf;
/**
* DHCP Relay Agent Application Component.
*/
@@ -105,7 +88,7 @@
@Service
public class DhcpRelayManager implements DhcpRelayService {
public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
- protected static final ProviderId PROVIDER_ID = new ProviderId("dhcp", DHCP_RELAY_APP);
+ public static final ProviderId PROVIDER_ID = new ProviderId("host", DHCP_RELAY_APP);
public static final String HOST_LOCATION_PROVIDER =
"org.onosproject.provider.host.impl.HostLocationProvider";
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -135,12 +118,6 @@
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostStore hostStore;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected RouteStore routeStore;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected InterfaceService interfaceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -149,21 +126,20 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService compCfgService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
+ target = "(version=4)")
+ protected DhcpHandler v4Handler;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
+ target = "(version=6)")
+ protected DhcpHandler v6Handler;
+
@Property(name = "arpEnabled", boolValue = true,
- label = "Enable Address resolution protocol")
+ label = "Enable Address resolution protocol")
protected boolean arpEnabled = true;
private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
private InternalHostListener hostListener = new InternalHostListener();
- private Ip4Address dhcpServerIp = null;
- // dhcp server may be connected directly to the SDN network or
- // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
- // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
- // to the gateway.
- private ConnectPoint dhcpServerConnectPoint = null;
- private MacAddress dhcpConnectMac = null;
- private VlanId dhcpConnectVlan = null;
- private Ip4Address dhcpGatewayIp = null;
private ApplicationId appId;
@Activate
@@ -175,8 +151,11 @@
factories.forEach(cfgService::registerConfigFactory);
//update the dhcp server configuration.
updateConfig();
- //add the packet services.
+
+ //add the packet processor
packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
+
+ // listen host event for dhcp server or the gateway
hostService.addListener(hostListener);
requestDhcpPackets();
modified(context);
@@ -196,11 +175,10 @@
hostService.removeListener(hostListener);
cancelDhcpPackets();
cancelArpPackets();
- if (dhcpGatewayIp != null) {
- hostService.stopMonitoringIp(dhcpGatewayIp);
- } else {
- hostService.stopMonitoringIp(dhcpServerIp);
- }
+ v4Handler.getDhcpGatewayIp().ifPresent(hostService::stopMonitoringIp);
+ v4Handler.getDhcpServerIp().ifPresent(hostService::stopMonitoringIp);
+ // TODO: DHCPv6 Handler
+
compCfgService.unregisterProperties(getClass(), true);
log.info("DHCP-RELAY Stopped");
}
@@ -224,44 +202,33 @@
}
}
- /**
- * Checks if this app has been configured.
- *
- * @return true if all information we need have been initialized
- */
- private boolean configured() {
- return dhcpServerConnectPoint != null && dhcpServerIp != null;
- }
-
private void updateConfig() {
DhcpRelayConfig cfg = cfgService.getConfig(appId, DhcpRelayConfig.class);
if (cfg == null) {
log.warn("Dhcp Server info not available");
return;
}
+ Optional<IpAddress> oldDhcpServerIp = v4Handler.getDhcpServerIp();
+ Optional<IpAddress> oldDhcpGatewayIp = v4Handler.getDhcpGatewayIp();
+ v4Handler.setDhcpServerConnectPoint(cfg.getDhcpServerConnectPoint());
+ v4Handler.setDhcpServerIp(cfg.getDhcpServerIp());
+ v4Handler.setDhcpGatewayIp(cfg.getDhcpGatewayIp());
+ v4Handler.setDhcpConnectMac(null);
+ v4Handler.setDhcpConnectVlan(null);
- dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
- Ip4Address oldDhcpServerIp = dhcpServerIp;
- Ip4Address oldDhcpGatewayIp = dhcpGatewayIp;
- dhcpServerIp = cfg.getDhcpServerIp();
- dhcpGatewayIp = cfg.getDhcpGatewayIp();
- dhcpConnectMac = null; // reset for updated config
- dhcpConnectVlan = null; // reset for updated config
- log.info("DHCP server connect point: " + dhcpServerConnectPoint);
- log.info("DHCP server ipaddress " + dhcpServerIp);
+ log.info("DHCP server connect point: " + cfg.getDhcpServerConnectPoint());
+ log.info("DHCP server ipaddress " + cfg.getDhcpServerIp());
- IpAddress ipToProbe = dhcpGatewayIp != null ? dhcpGatewayIp : dhcpServerIp;
- String hostToProbe = dhcpGatewayIp != null ? "gateway" : "DHCP server";
+ IpAddress ipToProbe = v4Handler.getDhcpGatewayIp().isPresent() ? cfg.getDhcpGatewayIp() :
+ cfg.getDhcpServerIp();
+ String hostToProbe = v4Handler.getDhcpGatewayIp().isPresent() ? "gateway" : "DHCP server";
+ // TODO: DHCPv6 server config
Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
if (hosts.isEmpty()) {
log.info("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
- if (oldDhcpGatewayIp != null) {
- hostService.stopMonitoringIp(oldDhcpGatewayIp);
- }
- if (oldDhcpServerIp != null) {
- hostService.stopMonitoringIp(oldDhcpServerIp);
- }
+ oldDhcpGatewayIp.ifPresent(hostService::stopMonitoringIp);
+ oldDhcpServerIp.ifPresent(hostService::stopMonitoringIp);
hostService.startMonitoringIp(ipToProbe);
} else {
// Probe target is known; There should be only 1 host with this ip
@@ -270,30 +237,39 @@
}
private void hostRemoved(Host host) {
- if (host.ipAddresses().contains(dhcpServerIp)) {
- log.warn("DHCP server {} removed", dhcpServerIp);
- dhcpConnectMac = null;
- dhcpConnectVlan = null;
- }
- if (dhcpGatewayIp != null && host.ipAddresses().contains(dhcpGatewayIp)) {
- log.warn("DHCP gateway {} removed", dhcpGatewayIp);
- dhcpConnectMac = null;
- dhcpConnectVlan = null;
- }
+ v4Handler.getDhcpServerIp().ifPresent(ip -> {
+ if (host.ipAddresses().contains(ip)) {
+ log.warn("DHCP server {} removed", ip);
+ v4Handler.setDhcpConnectMac(null);
+ v4Handler.setDhcpConnectVlan(null);
+ }
+ });
+ v4Handler.getDhcpGatewayIp().ifPresent(ip -> {
+ if (host.ipAddresses().contains(ip)) {
+ log.warn("DHCP gateway {} removed", ip);
+ v4Handler.setDhcpConnectMac(null);
+ v4Handler.setDhcpConnectVlan(null);
+ }
+ });
+ // TODO: v6 handler
}
private void hostUpdated(Host host) {
- if (dhcpGatewayIp != null && host.ipAddresses().contains(dhcpGatewayIp)) {
- dhcpConnectMac = host.mac();
- dhcpConnectVlan = host.vlan();
- log.info("DHCP gateway {} resolved to Mac/Vlan:{}/{}", dhcpGatewayIp,
- dhcpConnectMac, dhcpConnectVlan);
- } else if (host.ipAddresses().contains(dhcpServerIp)) {
- dhcpConnectMac = host.mac();
- dhcpConnectVlan = host.vlan();
- log.info("DHCP server {} resolved to Mac/Vlan:{}/{}", dhcpServerIp,
- dhcpConnectMac, dhcpConnectVlan);
- }
+ v4Handler.getDhcpGatewayIp().ifPresent(ip -> {
+ if (host.ipAddresses().contains(ip)) {
+ log.warn("DHCP gateway {} removed", ip);
+ v4Handler.setDhcpConnectMac(host.mac());
+ v4Handler.setDhcpConnectVlan(host.vlan());
+ }
+ });
+ v4Handler.getDhcpServerIp().ifPresent(ip -> {
+ if (host.ipAddresses().contains(ip)) {
+ log.warn("DHCP server {} removed", ip);
+ v4Handler.setDhcpConnectMac(host.mac());
+ v4Handler.setDhcpConnectVlan(host.vlan());
+ }
+ });
+ // TODO: v6 handler
}
/**
@@ -360,174 +336,7 @@
@Override
public Optional<MacAddress> getDhcpServerMacAddress() {
- return Optional.ofNullable(dhcpConnectMac);
- }
-
- /**
- * Gets output interface of a dhcp packet.
- * If option 82 exists in the dhcp packet and the option was sent by
- * ONOS (gateway address exists in ONOS interfaces), use the connect
- * point and vlan id from circuit id; otherwise, find host by destination
- * address and use vlan id from sender (dhcp server).
- *
- * @param ethPacket the ethernet packet
- * @param dhcpPayload the dhcp packet
- * @return an interface represent the output port and vlan; empty value
- * if the host or circuit id not found
- */
- private Optional<Interface> getOutputInterface(Ethernet ethPacket, DHCP dhcpPayload) {
- VlanId originalPacketVlanId = VlanId.vlanId(ethPacket.getVlanID());
- IpAddress gatewayIpAddress = Ip4Address.valueOf(dhcpPayload.getGatewayIPAddress());
- Set<Interface> gatewayInterfaces = interfaceService.getInterfacesByIp(gatewayIpAddress);
- DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
-
- // Sent by ONOS, and contains circuit id
- if (!gatewayInterfaces.isEmpty() && option != null) {
- DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
- try {
- CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
- ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(circuitId.connectPoint());
- VlanId vlanId = circuitId.vlanId();
- return Optional.of(new Interface(null, connectPoint, null, null, vlanId));
- } catch (IllegalArgumentException ex) {
- // invalid circuit format, didn't sent by ONOS
- log.debug("Invalid circuit {}, use information from dhcp payload",
- circuitIdSubOption.getData());
- }
- }
-
- // Use Vlan Id from DHCP server if DHCP relay circuit id was not
- // sent by ONOS or circuit Id can't be parsed
- MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
- Optional<DhcpRecord> dhcpRecord = dhcpRelayStore.getDhcpRecord(HostId.hostId(dstMac, originalPacketVlanId));
- return dhcpRecord
- .map(DhcpRecord::locations)
- .orElse(Collections.emptySet())
- .stream()
- .reduce((hl1, hl2) -> {
- if (hl1 == null || hl2 == null) {
- return hl1 == null ? hl2 : hl1;
- }
- return hl1.time() > hl2.time() ? hl1 : hl2;
- })
- .map(lastLocation -> new Interface(null, lastLocation, null, null, originalPacketVlanId));
- }
-
- /**
- * Send the response DHCP to the requester host.
- *
- * @param ethPacket the packet
- * @param dhcpPayload the DHCP data
- */
- private void handleDhcpOffer(Ethernet ethPacket, DHCP dhcpPayload) {
- Optional<Interface> outInterface = getOutputInterface(ethPacket, dhcpPayload);
- outInterface.ifPresent(theInterface -> {
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setOutput(theInterface.connectPoint().port())
- .build();
- OutboundPacket o = new DefaultOutboundPacket(
- theInterface.connectPoint().deviceId(),
- treatment,
- ByteBuffer.wrap(ethPacket.serialize()));
- if (log.isTraceEnabled()) {
- log.trace("Relaying packet to DHCP client {} via {}, vlan {}",
- ethPacket,
- theInterface.connectPoint(),
- theInterface.vlan());
- }
- packetService.emit(o);
- });
- }
-
- /**
- * Send the DHCP ack to the requester host.
- *
- * @param ethernetPacketAck the packet
- * @param dhcpPayload the DHCP data
- */
- private void handleDhcpAck(Ethernet ethernetPacketAck, DHCP dhcpPayload) {
- Optional<Interface> outInterface = getOutputInterface(ethernetPacketAck, dhcpPayload);
- if (!outInterface.isPresent()) {
- log.warn("Can't find output interface for dhcp: {}", dhcpPayload);
- return;
- }
-
- Interface outIface = outInterface.get();
- HostLocation hostLocation = new HostLocation(outIface.connectPoint(), System.currentTimeMillis());
- MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
- VlanId vlanId = outIface.vlan();
- HostId hostId = HostId.hostId(macAddress, vlanId);
- Ip4Address ip = Ip4Address.valueOf(dhcpPayload.getYourIPAddress());
-
- if (directlyConnected(dhcpPayload)) {
- // Add to host store if it connect to network directly
- Set<IpAddress> ips = Sets.newHashSet(ip);
- HostDescription desc = new DefaultHostDescription(macAddress, vlanId,
- hostLocation, ips);
-
- // Replace the ip when dhcp server give the host new ip address
- hostStore.createOrUpdateHost(PROVIDER_ID, hostId, desc, true);
- } else {
- // Add to route store if it does not connect to network directly
- // Get gateway host IP according to host mac address
- DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
-
- if (record == null) {
- log.warn("Can't find DHCP record of host {}", hostId);
- return;
- }
-
- MacAddress gwMac = record.nextHop().orElse(null);
- if (gwMac == null) {
- log.warn("Can't find gateway mac address from record {}", record);
- return;
- }
-
- HostId gwHostId = HostId.hostId(gwMac, record.vlanId());
- Host gwHost = hostService.getHost(gwHostId);
-
- if (gwHost == null) {
- log.warn("Can't find gateway host {}", gwHostId);
- return;
- }
-
- Ip4Address nextHopIp = gwHost.ipAddresses()
- .stream()
- .filter(IpAddress::isIp4)
- .map(IpAddress::getIp4Address)
- .findFirst()
- .orElse(null);
-
- if (nextHopIp == null) {
- log.warn("Can't find IP address of gateway {}", gwHost);
- return;
- }
-
- Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
- routeStore.updateRoute(route);
- }
- handleDhcpOffer(ethernetPacketAck, dhcpPayload);
- }
-
- /**
- * forward the packet to ConnectPoint where the DHCP server is attached.
- *
- * @param packet the packet
- */
- private void handleDhcpDiscoverAndRequest(Ethernet packet) {
- // send packet to dhcp server connect point.
- if (dhcpServerConnectPoint != null) {
- TrafficTreatment t = DefaultTrafficTreatment.builder()
- .setOutput(dhcpServerConnectPoint.port()).build();
- OutboundPacket o = new DefaultOutboundPacket(
- dhcpServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
- if (log.isTraceEnabled()) {
- log.trace("Relaying packet to dhcp server {}", packet);
- }
- packetService.emit(o);
- } else {
- log.warn("Can't find DHCP server connect point, abort.");
- }
+ return v4Handler.getDhcpConnectMac();
}
/**
@@ -572,41 +381,11 @@
.findFirst();
}
- /**
- * Check if the host is directly connected to the network or not.
- *
- * @param dhcpPayload the dhcp payload
- * @return true if the host is directly connected to the network; false otherwise
- */
- private boolean directlyConnected(DHCP dhcpPayload) {
- DhcpOption relayAgentOption = dhcpPayload.getOption(OptionCode_CircuitID);
-
- // Doesn't contains relay option
- if (relayAgentOption == null) {
- return true;
- }
-
- IpAddress gatewayIp = IpAddress.valueOf(dhcpPayload.getGatewayIPAddress());
- Set<Interface> gatewayInterfaces = interfaceService.getInterfacesByIp(gatewayIp);
-
- // Contains relay option, and added by ONOS
- if (!gatewayInterfaces.isEmpty()) {
- return true;
- }
-
- // Relay option added by other relay agent
- return false;
- }
private class DhcpRelayPacketProcessor implements PacketProcessor {
@Override
public void process(PacketContext context) {
- if (!configured()) {
- log.warn("Missing DHCP relay server config. Abort packet processing");
- return;
- }
-
// process the packet and get the payload
Ethernet packet = context.inPacket().parsed();
if (packet == null) {
@@ -614,12 +393,11 @@
}
findDhcp(packet).ifPresent(dhcpPayload -> {
- processDhcpPacket(context, dhcpPayload);
+ v4Handler.processDhcpPacket(context, dhcpPayload);
});
findDhcp6(packet).ifPresent(dhcp6Payload -> {
- // TODO: handle DHCPv6 packet
- log.warn("DHCPv6 unsupported.");
+ v6Handler.processDhcpPacket(context, dhcp6Payload);
});
if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
@@ -641,8 +419,7 @@
.map(Interface::mac)
.filter(mac -> !mac.equals(MacAddress.NONE))
.findFirst()
- .orElse(null);
-
+ .orElse(MacAddress.ONOS);
if (interfaceMac == null) {
// can't find interface mac address
return;
@@ -686,263 +463,6 @@
}
packetService.emit(o);
}
-
- // process the dhcp packet before sending to server
- private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
- ConnectPoint inPort = context.inPacket().receivedFrom();
- Set<Interface> clientServerInterfaces = interfaceService.getInterfacesByPort(inPort);
- // ignore the packets if dhcp client interface is not configured on onos.
- if (clientServerInterfaces.isEmpty()) {
- log.warn("Virtual interface is not configured on {}", inPort);
- return;
- }
- checkNotNull(dhcpPayload, "Can't find DHCP payload");
- Ethernet packet = context.inPacket().parsed();
- DHCP.MsgType incomingPacketType = dhcpPayload.getOptions().stream()
- .filter(dhcpOption -> dhcpOption.getCode() == OptionCode_MessageType.getValue())
- .map(DhcpOption::getData)
- .map(data -> DHCP.MsgType.getType(data[0]))
- .findFirst()
- .orElse(null);
- checkNotNull(incomingPacketType, "Can't get message type from DHCP payload {}", dhcpPayload);
- switch (incomingPacketType) {
- case DHCPDISCOVER:
- // add the gatewayip as virtual interface ip for server to understand
- // the lease to be assigned and forward the packet to dhcp server.
- Ethernet ethernetPacketDiscover =
- processDhcpPacketFromClient(context, packet, clientServerInterfaces);
-
- if (ethernetPacketDiscover != null) {
- writeRequestDhcpRecord(inPort, packet, dhcpPayload);
- handleDhcpDiscoverAndRequest(ethernetPacketDiscover);
- }
- break;
- case DHCPOFFER:
- //reply to dhcp client.
- Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
- if (ethernetPacketOffer != null) {
- writeResponseDhcpRecord(ethernetPacketOffer, dhcpPayload);
- handleDhcpOffer(ethernetPacketOffer, dhcpPayload);
- }
- break;
- case DHCPREQUEST:
- // add the gateway ip as virtual interface ip for server to understand
- // the lease to be assigned and forward the packet to dhcp server.
- Ethernet ethernetPacketRequest =
- processDhcpPacketFromClient(context, packet, clientServerInterfaces);
- if (ethernetPacketRequest != null) {
- writeRequestDhcpRecord(inPort, packet, dhcpPayload);
- handleDhcpDiscoverAndRequest(ethernetPacketRequest);
- }
- break;
- case DHCPACK:
- // reply to dhcp client.
- Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
- if (ethernetPacketAck != null) {
- writeResponseDhcpRecord(ethernetPacketAck, dhcpPayload);
- handleDhcpAck(ethernetPacketAck, dhcpPayload);
- }
- break;
- case DHCPRELEASE:
- // TODO: release the ip address from client
- break;
- default:
- break;
- }
- }
-
- private void writeRequestDhcpRecord(ConnectPoint location,
- Ethernet ethernet,
- DHCP dhcpPayload) {
- VlanId vlanId = VlanId.vlanId(ethernet.getVlanID());
- MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
- HostId hostId = HostId.hostId(macAddress, vlanId);
- DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
- if (record == null) {
- record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
- } else {
- record = record.clone();
- }
- record.addLocation(new HostLocation(location, System.currentTimeMillis()));
- record.ip4Status(dhcpPayload.getPacketType());
- record.setDirectlyConnected(directlyConnected(dhcpPayload));
- if (!directlyConnected(dhcpPayload)) {
- // Update gateway mac address if the host is not directly connected
- record.nextHop(ethernet.getSourceMAC());
- }
- record.updateLastSeen();
- dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
- }
-
- private void writeResponseDhcpRecord(Ethernet ethernet,
- DHCP dhcpPayload) {
- Optional<Interface> outInterface = getOutputInterface(ethernet, dhcpPayload);
- if (!outInterface.isPresent()) {
- log.warn("Failed to determine where to send {}", dhcpPayload.getPacketType());
- return;
- }
-
- Interface outIface = outInterface.get();
- ConnectPoint location = outIface.connectPoint();
- VlanId vlanId = outIface.vlan();
- MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
- HostId hostId = HostId.hostId(macAddress, vlanId);
- DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
- if (record == null) {
- record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
- } else {
- record = record.clone();
- }
- record.addLocation(new HostLocation(location, System.currentTimeMillis()));
- if (dhcpPayload.getPacketType() == DHCP.MsgType.DHCPACK) {
- record.ip4Address(Ip4Address.valueOf(dhcpPayload.getYourIPAddress()));
- }
- record.ip4Status(dhcpPayload.getPacketType());
- record.setDirectlyConnected(directlyConnected(dhcpPayload));
- record.updateLastSeen();
- dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
- }
-
- //build the DHCP discover/request packet with gatewayip(unicast packet)
- private Ethernet processDhcpPacketFromClient(PacketContext context,
- Ethernet ethernetPacket, Set<Interface> clientInterfaces) {
- Ip4Address relayAgentIp = getRelayAgentIPv4Address(clientInterfaces);
- MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
- if (relayAgentIp == null || relayAgentMac == null) {
- log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
- + "packet from client on port: {}. Aborting packet processing",
- clientInterfaces.iterator().next().connectPoint());
- return null;
- }
- if (dhcpConnectMac == null) {
- log.warn("DHCP {} not yet resolved .. Aborting DHCP "
- + "packet processing from client on port: {}",
- (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
- : "gateway IP " + dhcpGatewayIp,
- clientInterfaces.iterator().next().connectPoint());
- return null;
- }
- // get dhcp header.
- Ethernet etherReply = (Ethernet) ethernetPacket.clone();
- etherReply.setSourceMACAddress(relayAgentMac);
- etherReply.setDestinationMACAddress(dhcpConnectMac);
- etherReply.setVlanID(dhcpConnectVlan.toShort());
- IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
- ipv4Packet.setSourceAddress(relayAgentIp.toInt());
- ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
- UDP udpPacket = (UDP) ipv4Packet.getPayload();
- DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
-
- // If there is no relay agent option(option 82), add one to DHCP payload
- boolean containsRelayAgentOption = dhcpPacket.getOptions().stream()
- .map(DhcpOption::getCode)
- .anyMatch(code -> code == OptionCode_CircuitID.getValue());
-
- if (!containsRelayAgentOption) {
- ConnectPoint inPort = context.inPacket().receivedFrom();
- VlanId vlanId = VlanId.vlanId(ethernetPacket.getVlanID());
- // add connected in port and vlan
- CircuitId cid = new CircuitId(inPort.toString(), vlanId);
- byte[] circuitId = cid.serialize();
-
- if (circuitId != null) {
- DhcpOption circuitIdSubOpt = new DhcpOption();
- circuitIdSubOpt
- .setCode(CIRCUIT_ID.getValue())
- .setLength((byte) circuitId.length)
- .setData(circuitId);
-
- DhcpRelayAgentOption newRelayAgentOpt = new DhcpRelayAgentOption();
- newRelayAgentOpt.setCode(OptionCode_CircuitID.getValue());
- newRelayAgentOpt.addSubOption(circuitIdSubOpt);
-
- // push new circuit id to last
- List<DhcpOption> options = dhcpPacket.getOptions();
- options.add(newRelayAgentOpt);
-
- // make sure option 255(End) is the last option
- options.sort((opt1, opt2) -> opt2.getCode() - opt1.getCode());
- dhcpPacket.setOptions(options);
-
- dhcpPacket.setGatewayIPAddress(relayAgentIp.toInt());
- } else {
- log.warn("Can't generate circuit id for port {}, vlan {}", inPort, vlanId);
- }
- } else {
- // TODO: if it contains a relay agent information, sets gateway ip
- // according to the information it provided
- }
-
- udpPacket.setPayload(dhcpPacket);
- udpPacket.setSourcePort(UDP.DHCP_CLIENT_PORT);
- udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
- ipv4Packet.setPayload(udpPacket);
- etherReply.setPayload(ipv4Packet);
- return etherReply;
- }
-
- // Returns the first v4 interface ip out of a set of interfaces or null.
- // Checks all interfaces, and ignores v6 interface ips
- private Ip4Address getRelayAgentIPv4Address(Set<Interface> intfs) {
- for (Interface intf : intfs) {
- for (InterfaceIpAddress ip : intf.ipAddressesList()) {
- Ip4Address relayAgentIp = ip.ipAddress().getIp4Address();
- if (relayAgentIp != null) {
- return relayAgentIp;
- }
- }
- }
- return null;
- }
-
- //build the DHCP offer/ack with proper client port.
- private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
- // get dhcp header.
- Ethernet etherReply = (Ethernet) ethernetPacket.clone();
- IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
- UDP udpPacket = (UDP) ipv4Packet.getPayload();
- DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
-
- // determine the vlanId of the client host - note that this vlan id
- // could be different from the vlan in the packet from the server
- Interface outInterface = getOutputInterface(ethernetPacket, dhcpPayload).orElse(null);
-
- if (outInterface == null) {
- log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
- return null;
- }
-
- etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
- etherReply.setVlanID(outInterface.vlan().toShort());
- // we leave the srcMac from the original packet
-
- // figure out the relay agent IP corresponding to the original request
- Ip4Address relayAgentIP = getRelayAgentIPv4Address(
- interfaceService.getInterfacesByPort(outInterface.connectPoint()));
- if (relayAgentIP == null) {
- log.warn("Cannot determine relay agent interface Ipv4 addr for host {}/{}. "
- + "Aborting relay for dhcp packet from server {}",
- etherReply.getDestinationMAC(), outInterface.vlan(),
- ethernetPacket);
- return null;
- }
- // SRC_IP: relay agent IP
- // DST_IP: offered IP
- ipv4Packet.setSourceAddress(relayAgentIP.toInt());
- ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
- udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
- if (directlyConnected(dhcpPayload)) {
- udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
- } else {
- // forward to another dhcp relay
- udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
- }
-
- udpPacket.setPayload(dhcpPayload);
- ipv4Packet.setPayload(udpPacket);
- etherReply.setPayload(ipv4Packet);
- return etherReply;
- }
}
/**
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
new file mode 100644
index 0000000..d015532
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-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.dhcprelay.api;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.packet.PacketContext;
+
+import java.util.Optional;
+
+/**
+ * DHCP relay handler.
+ */
+public interface DhcpHandler {
+ /**
+ * Process the DHCP packet before sending to server or client.
+ *
+ * @param context the packet context
+ * @param dhcpPayload the DHCP payload
+ */
+ void processDhcpPacket(PacketContext context, BasePacket dhcpPayload);
+
+ /**
+ * Gets DHCP server IP.
+ *
+ * @return IP address of DHCP server; empty value if not exist
+ */
+ Optional<IpAddress> getDhcpServerIp();
+
+ /**
+ * Gets DHCP gateway IP.
+ *
+ * @return IP address of DHCP gateway; empty value if not exist
+ */
+ Optional<IpAddress> getDhcpGatewayIp();
+
+ /**
+ * Gets DHCP connect Mac address.
+ *
+ * @return the connect Mac address of server or gateway
+ */
+ Optional<MacAddress> getDhcpConnectMac();
+
+ /**
+ * Sets DHCP gateway IP.
+ *
+ * @param dhcpGatewayIp the DHCP gateway IP
+ */
+ void setDhcpGatewayIp(IpAddress dhcpGatewayIp);
+
+ /**
+ * Sets DHCP connect vlan.
+ *
+ * @param dhcpConnectVlan the DHCP connect vlan
+ */
+ void setDhcpConnectVlan(VlanId dhcpConnectVlan);
+
+ /**
+ * Sets DHCP connect Mac address.
+ *
+ * @param dhcpConnectMac the connect Mac address
+ */
+ void setDhcpConnectMac(MacAddress dhcpConnectMac);
+
+ /**
+ * Sets DHCP server connect point.
+ *
+ * @param dhcpServerConnectPoint the server connect point
+ */
+ void setDhcpServerConnectPoint(ConnectPoint dhcpServerConnectPoint);
+
+ /**
+ * Sets DHCP server IP.
+ *
+ * @param dhcpServerIp the DHCP server IP
+ */
+ void setDhcpServerIp(IpAddress dhcpServerIp);
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayService.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpRelayService.java
similarity index 96%
rename from apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayService.java
rename to apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpRelayService.java
index c4ab2d1..9bad009 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayService.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpRelayService.java
@@ -12,9 +12,10 @@
* 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.dhcprelay;
+package org.onosproject.dhcprelay.api;
import org.onlab.packet.MacAddress;
import org.onosproject.dhcprelay.store.DhcpRecord;
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/package-info.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/package-info.java
new file mode 100644
index 0000000..fa239e8
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017-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.
+ *
+ */
+
+/**
+ * APIs for DHCP relay application.
+ */
+package org.onosproject.dhcprelay.api;
\ No newline at end of file
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
index be4b75c..089b8ba 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
@@ -27,7 +27,7 @@
import org.onosproject.core.CoreService;
import org.onosproject.dhcprelay.DhcpRelayConfig;
import org.onosproject.dhcprelay.DhcpRelayManager;
-import org.onosproject.dhcprelay.DhcpRelayService;
+import org.onosproject.dhcprelay.api.DhcpRelayService;
import org.onosproject.dhcprelay.store.DhcpRecord;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Host;
diff --git a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
index 784524a..3b8e08c 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -186,13 +186,20 @@
mockHostStore = new MockHostStore();
mockRouteStore = new MockRouteStore();
mockDhcpRelayStore = new MockDhcpRelayStore();
-
- manager.hostStore = mockHostStore;
- manager.routeStore = mockRouteStore;
manager.dhcpRelayStore = mockDhcpRelayStore;
manager.interfaceService = new MockInterfaceService();
+ Dhcp4HandlerImpl v4Handler = new Dhcp4HandlerImpl();
+ v4Handler.dhcpRelayStore = mockDhcpRelayStore;
+ v4Handler.hostService = manager.hostService;
+ v4Handler.hostStore = mockHostStore;
+ v4Handler.interfaceService = manager.interfaceService;
+ v4Handler.packetService = manager.packetService;
+ v4Handler.routeStore = mockRouteStore;
+ manager.v4Handler = v4Handler;
+
+ // TODO: initialize v6 handler.
// properties
Dictionary<String, Object> dictionary = createNiceMock(Dictionary.class);
expect(dictionary.get("arpEnabled")).andReturn(true).anyTimes();