[CORD-1434][CORD-1112] DHCP relay manager
Change-Id: I2e4d8fc8e85ed66b33ac517660ee72a1c0183597
diff --git a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
index 996bd43..e197a0d 100644
--- a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
@@ -43,6 +43,7 @@
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
@@ -77,7 +78,10 @@
import java.nio.ByteBuffer;
import java.util.Dictionary;
+import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.ExecutorService;
+import java.util.stream.Stream;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
@@ -89,7 +93,6 @@
*/
@Component(immediate = true)
public class HostLocationProvider extends AbstractProvider implements HostProvider {
-
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -113,6 +116,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
private HostProviderService providerService;
private final InternalHostProvider processor = new InternalHostProvider();
@@ -124,18 +130,18 @@
label = "Enable host removal on port/device down events")
private boolean hostRemovalEnabled = true;
- @Property(name = "useArp", boolValue = true,
- label = "Enable using ARP for neighbor discovery by the " +
+ @Property(name = "requestArp", boolValue = true,
+ label = "Request ARP packets for neighbor discovery by the " +
"Host Location Provider; default is true")
- private boolean useArp = true;
+ private boolean requestArp = true;
- @Property(name = "useIpv6ND", boolValue = false,
- label = "Enable using IPv6 Neighbor Discovery by the " +
+ @Property(name = "requestIpv6ND", boolValue = false,
+ label = "Requests IPv6 Neighbor Discovery by the " +
"Host Location Provider; default is false")
- private boolean useIpv6ND = false;
+ private boolean requestIpv6ND = false;
@Property(name = "useDhcp", boolValue = false,
- label = "Enable using DHCP for neighbor discovery by the " +
+ label = "Use DHCP for neighbor discovery by the " +
"Host Location Provider; default is false")
private boolean useDhcp = false;
@@ -202,7 +208,7 @@
TrafficSelector arpSelector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_ARP)
.build();
- if (useArp) {
+ if (requestArp) {
packetService.requestPackets(arpSelector, PacketPriority.CONTROL, appId);
} else {
packetService.cancelPackets(arpSelector, PacketPriority.CONTROL, appId);
@@ -219,7 +225,7 @@
.matchIPProtocol(IPv6.PROTOCOL_ICMP6)
.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT)
.build();
- if (useIpv6ND) {
+ if (requestIpv6ND) {
packetService.requestPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
packetService.requestPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
} else {
@@ -238,13 +244,6 @@
.matchIPProtocol(IPv4.PROTOCOL_UDP)
.matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
.build();
- if (useDhcp) {
- packetService.requestPackets(dhcpServerSelector, PacketPriority.CONTROL, appId);
- packetService.requestPackets(dhcpClientSelector, PacketPriority.CONTROL, appId);
- } else {
- packetService.cancelPackets(dhcpServerSelector, PacketPriority.CONTROL, appId);
- packetService.cancelPackets(dhcpClientSelector, PacketPriority.CONTROL, appId);
- }
}
/**
@@ -278,51 +277,51 @@
flag = Tools.isPropertyEnabled(properties, "hostRemovalEnabled");
if (flag == null) {
log.info("Host removal on port/device down events is not configured, " +
- "using current value of {}", hostRemovalEnabled);
+ "using current value of {}", hostRemovalEnabled);
} else {
hostRemovalEnabled = flag;
log.info("Configured. Host removal on port/device down events is {}",
hostRemovalEnabled ? "enabled" : "disabled");
}
- flag = Tools.isPropertyEnabled(properties, "useArp");
+ flag = Tools.isPropertyEnabled(properties, "requestArp");
if (flag == null) {
log.info("Using ARP is not configured, " +
- "using current value of {}", useArp);
+ "using current value of {}", requestArp);
} else {
- useArp = flag;
+ requestArp = flag;
log.info("Configured. Using ARP is {}",
- useArp ? "enabled" : "disabled");
+ requestArp ? "enabled" : "disabled");
}
- flag = Tools.isPropertyEnabled(properties, "useIpv6ND");
+ flag = Tools.isPropertyEnabled(properties, "requestIpv6ND");
if (flag == null) {
log.info("Using IPv6 Neighbor Discovery is not configured, " +
- "using current value of {}", useIpv6ND);
+ "using current value of {}", requestIpv6ND);
} else {
- useIpv6ND = flag;
+ requestIpv6ND = flag;
log.info("Configured. Using IPv6 Neighbor Discovery is {}",
- useIpv6ND ? "enabled" : "disabled");
+ requestIpv6ND ? "enabled" : "disabled");
}
flag = Tools.isPropertyEnabled(properties, "useDhcp");
if (flag == null) {
log.info("Using DHCP is not configured, " +
- "using current value of {}", useDhcp);
+ "using current value of {}", useDhcp);
} else {
useDhcp = flag;
log.info("Configured. Using DHCP is {}",
- useDhcp ? "enabled" : "disabled");
+ useDhcp ? "enabled" : "disabled");
}
flag = Tools.isPropertyEnabled(properties, "requestInterceptsEnabled");
if (flag == null) {
log.info("Request intercepts is not configured, " +
- "using current value of {}", requestInterceptsEnabled);
+ "using current value of {}", requestInterceptsEnabled);
} else {
requestInterceptsEnabled = flag;
log.info("Configured. Request intercepts is {}",
- requestInterceptsEnabled ? "enabled" : "disabled");
+ requestInterceptsEnabled ? "enabled" : "disabled");
}
}
@@ -475,30 +474,12 @@
createOrUpdateHost(hid, srcMac, vlan, hloc, ip);
// IPv4: update location only
- // DHCP ACK: additionally update IP of DHCP client
} else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
- IPacket pkt = eth.getPayload();
- if (pkt != null && pkt instanceof IPv4) {
- pkt = pkt.getPayload();
- if (pkt != null && pkt instanceof UDP) {
- pkt = pkt.getPayload();
- if (pkt != null && pkt instanceof DHCP) {
- DHCP dhcp = (DHCP) pkt;
- if (dhcp.getOptions().stream()
- .anyMatch(dhcpOption -> dhcpOption.getCode() ==
- DHCP.DHCPOptionCode.OptionCode_MessageType.getValue() &&
- dhcpOption.getLength() == 1 &&
- dhcpOption.getData()[0] == DHCP.MsgType.DHCPACK.getValue())) {
- MacAddress hostMac = MacAddress.valueOf(dhcp.getClientHardwareAddress());
- VlanId hostVlan = VlanId.vlanId(eth.getVlanID());
- HostId hostId = HostId.hostId(hostMac, hostVlan);
- updateHostIp(hostId, IpAddress.valueOf(dhcp.getYourIPAddress()));
- }
- }
- }
+ // DHCP ACK: additionally update IP of DHCP client
+ Optional<DHCP> dhcp = findDhcp(eth);
+ if (useDhcp || !dhcp.isPresent()) {
+ createOrUpdateHost(hid, srcMac, vlan, hloc, null);
}
- createOrUpdateHost(hid, srcMac, vlan, hloc, null);
-
//
// NeighborAdvertisement and NeighborSolicitation: possible
// new hosts, update both location and IP.
@@ -546,6 +527,21 @@
createOrUpdateHost(hid, srcMac, vlan, hloc, null);
}
}
+
+ private Optional<DHCP> findDhcp(Ethernet eth) {
+ IPacket pkt = eth.getPayload();
+ return Stream.of(pkt)
+ .filter(Objects::nonNull)
+ .filter(p -> p instanceof IPv4)
+ .map(IPacket::getPayload)
+ .filter(Objects::nonNull)
+ .filter(p -> p instanceof UDP)
+ .map(IPacket::getPayload)
+ .filter(Objects::nonNull)
+ .filter(p -> p instanceof DHCP)
+ .map(p -> (DHCP) p)
+ .findFirst();
+ }
}
// Auxiliary listener to device events.
diff --git a/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java b/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
index b28ec17..080e0e4 100644
--- a/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
+++ b/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
@@ -15,16 +15,19 @@
*/
package org.onosproject.provider.host.impl;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.onlab.junit.TestUtils;
import org.onlab.osgi.ComponentContextAdapter;
import org.onlab.packet.ARP;
import org.onlab.packet.ChassisId;
import org.onlab.packet.DHCP;
+import org.onlab.packet.dhcp.CircuitId;
import org.onlab.packet.dhcp.DhcpOption;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP6;
@@ -35,6 +38,7 @@
import org.onlab.packet.MacAddress;
import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
+import org.onlab.packet.dhcp.DhcpRelayAgentOption;
import org.onlab.packet.ndp.NeighborAdvertisement;
import org.onlab.packet.ndp.NeighborSolicitation;
import org.onlab.packet.ndp.RouterAdvertisement;
@@ -43,6 +47,9 @@
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceListener;
+import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultDevice;
import org.onosproject.net.DefaultHost;
@@ -60,6 +67,7 @@
import org.onosproject.net.host.HostProviderRegistry;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.packet.DefaultInboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.PacketContextAdapter;
@@ -85,7 +93,6 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
import static org.onlab.packet.VlanId.vlanId;
import static org.onosproject.net.Device.Type.SWITCH;
import static org.onosproject.net.DeviceId.deviceId;
@@ -109,6 +116,7 @@
private static final String DEV6 = "of:6";
private static final VlanId VLAN = vlanId();
+ private static final VlanId VLAN_100 = VlanId.vlanId("100");
// IPv4 Host
private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
@@ -122,6 +130,10 @@
new DefaultHost(PROVIDER_ID, hostId(MAC), MAC,
VLAN, LOCATION,
ImmutableSet.of(IP_ADDRESS));
+ private static final DefaultHost HOST_VLAN_100 =
+ new DefaultHost(PROVIDER_ID, hostId(MAC, VLAN_100), MAC,
+ VLAN_100, LOCATION,
+ ImmutableSet.of(IP_ADDRESS));
// IPv6 Host
private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
@@ -149,6 +161,14 @@
VLAN, LOCATION3,
ImmutableSet.of(IP_ADDRESS3));
+ // Gateway information for relay agent
+ private static final InterfaceIpAddress GW_IFACE_ADDR = InterfaceIpAddress.valueOf("10.0.1.1/32");
+ private static final Interface GW_IFACE = new Interface("gateway",
+ LOCATION,
+ ImmutableList.of(GW_IFACE_ADDR),
+ null,
+ VLAN_100);
+
private static final ComponentContextAdapter CTX_FOR_REMOVE =
new ComponentContextAdapter() {
@Override
@@ -173,6 +193,7 @@
private final TestDeviceService deviceService = new TestDeviceService();
private final TestHostService hostService = new TestHostService();
private final TestPacketService packetService = new TestPacketService();
+ private final TestInterfaceService interfaceService = new TestInterfaceService();
private PacketProcessor testProcessor;
private CoreService coreService;
@@ -183,7 +204,6 @@
@Before
public void setUp() {
-
coreService = createMock(CoreService.class);
expect(coreService.registerApplication(appId.name()))
.andReturn(appId).anyTimes();
@@ -197,6 +217,7 @@
provider.packetService = packetService;
provider.deviceService = deviceService;
provider.hostService = hostService;
+ provider.interfaceService = interfaceService;
provider.activate(CTX_FOR_NO_REMOVE);
@@ -335,8 +356,9 @@
*/
@Test
public void receiveDhcp() {
+ TestUtils.setField(provider, "useDhcp", true);
// DHCP Request
- testProcessor.process(new TestDhcpRequestPacketContext(DEV1));
+ testProcessor.process(new TestDhcpRequestPacketContext(DEV1, VLAN));
assertThat("receiveDhcpRequest. One host description expected",
providerService.descriptions.size(), is(1));
// Should learn the MAC and location of DHCP client
@@ -347,23 +369,30 @@
assertThat(descr.vlan(), is(VLAN));
// DHCP Ack
- testProcessor.process(new TestDhcpAckPacketContext(DEV1));
+ testProcessor.process(new TestDhcpAckPacketContext(DEV1, false));
assertThat("receiveDhcpAck. Two additional host descriptions expected",
- providerService.descriptions.size(), is(3));
- // Should learn the IP of DHCP client
- HostDescription descr2 = providerService.descriptions.get(1);
- assertThat(descr2.location(), is(LOCATION));
- assertThat(descr2.hwAddress(), is(MAC));
- assertThat(descr2.ipAddress().size(), is(1));
- assertTrue(descr2.ipAddress().contains(IP_ADDRESS));
- assertThat(descr2.vlan(), is(VLAN));
- // Should also learn the MAC, location of DHCP server
- HostDescription descr3 = providerService.descriptions.get(2);
- assertThat(descr3.location(), is(LOCATION3));
- assertThat(descr3.hwAddress(), is(MAC3));
- assertThat(descr3.ipAddress().size(), is(0));
- assertThat(descr3.vlan(), is(VLAN));
+ providerService.descriptions.size(), is(2));
+ // Should also learn the MAC, location of DHCP server
+ HostDescription descr2 = providerService.descriptions.get(1);
+ assertThat(descr2.location(), is(LOCATION3));
+ assertThat(descr2.hwAddress(), is(MAC3));
+ assertThat(descr2.ipAddress().size(), is(0));
+ assertThat(descr2.vlan(), is(VLAN));
+
+ // Should not update the IP address of the host.
+ }
+
+ /**
+ * The host store should not updated when we disabled "useDhcp".
+ */
+ @Test
+ public void receiveDhcpButNotEnabled() {
+ TestUtils.setField(provider, "useDhcp", false);
+ // DHCP Request
+ testProcessor.process(new TestDhcpRequestPacketContext(DEV1, VLAN));
+ assertThat("receiveDhcpButNotEnabled. No host description expected",
+ providerService.descriptions.size(), is(0));
}
/**
@@ -604,10 +633,12 @@
*/
private class TestDhcpRequestPacketContext extends PacketContextAdapter {
private final String deviceId;
+ private final VlanId vlanId;
- public TestDhcpRequestPacketContext(String deviceId) {
+ public TestDhcpRequestPacketContext(String deviceId, VlanId vlanId) {
super(0, null, null, false);
this.deviceId = deviceId;
+ this.vlanId = vlanId;
}
@Override
@@ -632,7 +663,7 @@
ipv4.setSourceAddress(IP_ADDRESS.toString());
Ethernet eth = new Ethernet();
eth.setEtherType(Ethernet.TYPE_IPV4)
- .setVlanID(VLAN.toShort())
+ .setVlanID(this.vlanId.toShort())
.setSourceMACAddress(MAC)
.setDestinationMACAddress(MAC3)
.setPayload(ipv4);
@@ -648,10 +679,12 @@
*/
private class TestDhcpAckPacketContext extends PacketContextAdapter {
private final String deviceId;
+ private final boolean withRelayinfo;
- public TestDhcpAckPacketContext(String deviceId) {
+ public TestDhcpAckPacketContext(String deviceId, boolean withRelayInfo) {
super(0, null, null, false);
this.deviceId = deviceId;
+ this.withRelayinfo = withRelayInfo;
}
@Override
@@ -663,10 +696,29 @@
dhcpOption.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
dhcpOption.setData(dhcpMsgType);
dhcpOption.setLength((byte) 1);
+
DHCP dhcp = new DHCP();
- dhcp.setOptions(Collections.singletonList(dhcpOption));
+
+ if (withRelayinfo) {
+ CircuitId cid = new CircuitId(LOCATION.toString(), VLAN_100);
+ byte[] circuitId = cid.serialize();
+ DhcpOption circuitIdSubOption = new DhcpOption();
+ circuitIdSubOption.setCode(DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID.getValue());
+ circuitIdSubOption.setData(circuitId);
+ circuitIdSubOption.setLength((byte) circuitId.length);
+
+ DhcpRelayAgentOption relayInfoOption = new DhcpRelayAgentOption();
+ relayInfoOption.setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue());
+ relayInfoOption.addSubOption(circuitIdSubOption);
+ dhcp.setOptions(ImmutableList.of(dhcpOption, relayInfoOption));
+ dhcp.setGatewayIPAddress(GW_IFACE_ADDR.ipAddress().getIp4Address().toInt());
+ } else {
+ dhcp.setOptions(ImmutableList.of(dhcpOption));
+ }
+
dhcp.setClientHardwareAddress(MAC.toBytes());
dhcp.setYourIPAddress(IP_ADDRESS.getIp4Address().toInt());
+
UDP udp = new UDP();
udp.setPayload(dhcp);
udp.setSourcePort(UDP.DHCP_SERVER_PORT);
@@ -961,9 +1013,60 @@
return HOST2;
} else if (hostId.equals(HostId.hostId(MAC3, VLAN))) {
return HOST3;
+ } else if (hostId.equals(HostId.hostId(MAC, VLAN_100))) {
+ return HOST_VLAN_100;
}
return null;
}
+ }
+ private class TestInterfaceService implements InterfaceService {
+ @Override
+ public Set<Interface> getInterfaces() {
+ return null;
+ }
+
+ @Override
+ public Interface getInterfaceByName(ConnectPoint connectPoint, String name) {
+ return null;
+ }
+
+ @Override
+ public Set<Interface> getInterfacesByPort(ConnectPoint port) {
+ return null;
+ }
+
+ public Set<Interface> getInterfacesByIp(IpAddress ip) {
+ if (ip.equals(GW_IFACE_ADDR.ipAddress())) {
+ return ImmutableSet.of(GW_IFACE);
+ } else {
+ return ImmutableSet.of();
+ }
+ }
+
+ @Override
+ public Set<Interface> getInterfacesByVlan(VlanId vlan) {
+ return null;
+ }
+
+ @Override
+ public Interface getMatchingInterface(IpAddress ip) {
+ return null;
+ }
+
+ @Override
+ public Set<Interface> getMatchingInterfaces(IpAddress ip) {
+ return null;
+ }
+
+ @Override
+ public void addListener(InterfaceListener listener) {
+
+ }
+
+ @Override
+ public void removeListener(InterfaceListener listener) {
+
+ }
}
}