Use DHCP ACK to learn the DHCP client
This feature can be turn on/off via component config.
In addition, ARP interception can also be turned on/off now.
Change-Id: Ia3310fa3fb06821051fbf3363e51096d00781dbf
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 d91d437..1119eda 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
@@ -23,12 +23,17 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ARP;
+import org.onlab.packet.DHCP;
+import org.onlab.packet.DHCPPacketType;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP6;
import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
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.ipv6.IExtensionHeader;
import org.onlab.packet.ndp.NeighborAdvertisement;
@@ -120,10 +125,20 @@
label = "Enable host removal on port/device down events")
private boolean hostRemovalEnabled = true;
- @Property(name = "ipv6NeighborDiscovery", boolValue = false,
+ @Property(name = "useArp", boolValue = true,
+ label = "Enable using ARP for neighbor discovery by the " +
+ "Host Location Provider; default is true")
+ private boolean useArp = true;
+
+ @Property(name = "useIpv6ND", boolValue = false,
label = "Enable using IPv6 Neighbor Discovery by the " +
"Host Location Provider; default is false")
- private boolean ipv6NeighborDiscovery = false;
+ private boolean useIpv6ND = false;
+
+ @Property(name = "useDhcp", boolValue = false,
+ label = "Enable using DHCP for neighbor discovery by the " +
+ "Host Location Provider; default is false")
+ private boolean useDhcp = false;
@Property(name = "requestInterceptsEnabled", boolValue = true,
label = "Enable requesting packet intercepts")
@@ -184,26 +199,52 @@
* Request packet intercepts.
*/
private void requestIntercepts() {
- TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
- selector.matchEthType(Ethernet.TYPE_ARP);
- packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
-
- // IPv6 Neighbor Solicitation packet.
- selector.matchEthType(Ethernet.TYPE_IPV6);
- selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
- selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
- if (ipv6NeighborDiscovery) {
- packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+ // Use ARP
+ TrafficSelector arpSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_ARP)
+ .build();
+ if (useArp) {
+ packetService.requestPackets(arpSelector, PacketPriority.CONTROL, appId);
} else {
- packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ packetService.cancelPackets(arpSelector, PacketPriority.CONTROL, appId);
}
- // IPv6 Neighbor Advertisement packet.
- selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
- if (ipv6NeighborDiscovery) {
- packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+ // Use IPv6 Neighbor Discovery
+ TrafficSelector ipv6NsSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV6)
+ .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
+ .matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION)
+ .build();
+ TrafficSelector ipv6NaSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV6)
+ .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
+ .matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT)
+ .build();
+ if (useIpv6ND) {
+ packetService.requestPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
+ packetService.requestPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
} else {
- packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ packetService.cancelPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
+ packetService.cancelPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
+ }
+
+ // Use DHCP
+ TrafficSelector dhcpServerSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_UDP)
+ .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+ .build();
+ TrafficSelector dhcpClientSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .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);
}
}
@@ -245,14 +286,34 @@
hostRemovalEnabled ? "enabled" : "disabled");
}
- flag = Tools.isPropertyEnabled(properties, "ipv6NeighborDiscovery");
+ flag = Tools.isPropertyEnabled(properties, "useArp");
+ if (flag == null) {
+ log.info("Using ARP is not configured, " +
+ "using current value of {}", useArp);
+ } else {
+ useArp = flag;
+ log.info("Configured. Using ARP is {}",
+ useArp ? "enabled" : "disabled");
+ }
+
+ flag = Tools.isPropertyEnabled(properties, "useIpv6ND");
if (flag == null) {
log.info("Using IPv6 Neighbor Discovery is not configured, " +
- "using current value of {}", ipv6NeighborDiscovery);
+ "using current value of {}", useIpv6ND);
} else {
- ipv6NeighborDiscovery = flag;
+ useIpv6ND = flag;
log.info("Configured. Using IPv6 Neighbor Discovery is {}",
- ipv6NeighborDiscovery ? "enabled" : "disabled");
+ useIpv6ND ? "enabled" : "disabled");
+ }
+
+ flag = Tools.isPropertyEnabled(properties, "useDhcp");
+ if (flag == null) {
+ log.info("Using DHCP is not configured, " +
+ "using current value of {}", useDhcp);
+ } else {
+ useDhcp = flag;
+ log.info("Configured. Using DHCP is {}",
+ useDhcp ? "enabled" : "disabled");
}
flag = Tools.isPropertyEnabled(properties, "requestInterceptsEnabled");
@@ -332,7 +393,7 @@
private class InternalHostProvider implements PacketProcessor {
/**
- * Update host location only.
+ * Updates host location only.
*
* @param hid host ID
* @param mac source Mac address
@@ -350,7 +411,7 @@
}
/**
- * Update host location and IP address.
+ * Updates host location and IP address.
*
* @param hid host ID
* @param mac source Mac address
@@ -371,6 +432,28 @@
}
}
+ /**
+ * Updates host IP address for an existing host.
+ *
+ * @param hid host ID
+ * @param ip IP address
+ */
+ private void updateIp(HostId hid, IpAddress ip) {
+ Host host = hostService.getHost(hid);
+ if (host == null) {
+ log.debug("Fail to update IP for {}. Host does not exist");
+ return;
+ }
+
+ HostDescription desc =
+ new DefaultHostDescription(hid.mac(), hid.vlanId(), host.location(), ip);
+ try {
+ providerService.hostDetected(hid, desc, false);
+ } catch (IllegalStateException e) {
+ log.debug("Host {} suppressed", hid);
+ }
+ }
+
@Override
public void process(PacketContext context) {
if (context == null) {
@@ -412,7 +495,28 @@
updateLocationIP(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] == DHCPPacketType.DHCPACK.getValue())) {
+ MacAddress hostMac = MacAddress.valueOf(dhcp.getClientHardwareAddress());
+ VlanId hostVlan = VlanId.vlanId(eth.getVlanID());
+ HostId hostId = HostId.hostId(hostMac, hostVlan);
+ updateIp(hostId, IpAddress.valueOf(dhcp.getYourIPAddress()));
+ }
+ }
+ }
+ }
updateLocation(hid, srcMac, vlan, hloc);
//
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 acf0a13..10a12ab 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
@@ -26,13 +26,19 @@
import org.onlab.osgi.ComponentContextAdapter;
import org.onlab.packet.ARP;
import org.onlab.packet.ChassisId;
+import org.onlab.packet.DHCP;
+import org.onlab.packet.DHCPOption;
+import org.onlab.packet.DHCPPacketType;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
+import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
import org.onlab.packet.ndp.NeighborAdvertisement;
import org.onlab.packet.ndp.NeighborSolicitation;
@@ -93,6 +99,7 @@
new ProviderId("of", "org.onosproject.provider.host");
private static final Integer INPORT = 10;
+ private static final Integer INPORT2 = 11;
private static final String DEV1 = "of:1";
private static final String DEV2 = "of:2";
private static final String DEV3 = "of:3";
@@ -112,8 +119,8 @@
new HostLocation(deviceId(DEV1), portNumber(INPORT), 0L);
private static final DefaultHost HOST =
new DefaultHost(PROVIDER_ID, hostId(MAC), MAC,
- vlanId(VlanId.UNTAGGED), LOCATION,
- ImmutableSet.of(IP_ADDRESS));
+ VLAN, LOCATION,
+ ImmutableSet.of(IP_ADDRESS));
// IPv6 Host
private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
@@ -125,8 +132,21 @@
new HostLocation(deviceId(DEV4), portNumber(INPORT), 0L);
private static final DefaultHost HOST2 =
new DefaultHost(PROVIDER_ID, hostId(MAC2), MAC2,
- vlanId(VlanId.UNTAGGED), LOCATION2,
- ImmutableSet.of(IP_ADDRESS2));
+ VLAN, LOCATION2,
+ ImmutableSet.of(IP_ADDRESS2));
+
+ // DHCP Server
+ private static final MacAddress MAC3 = MacAddress.valueOf("00:00:33:00:00:03");
+ private static final byte[] IP3 = new byte[]{10, 0, 0, 2};
+ private static final IpAddress IP_ADDRESS3 =
+ IpAddress.valueOf(IpAddress.Version.INET, IP3);
+
+ private static final HostLocation LOCATION3 =
+ new HostLocation(deviceId(DEV1), portNumber(INPORT2), 0L);
+ private static final DefaultHost HOST3 =
+ new DefaultHost(PROVIDER_ID, hostId(MAC3), MAC3,
+ VLAN, LOCATION3,
+ ImmutableSet.of(IP_ADDRESS3));
private static final ComponentContextAdapter CTX_FOR_REMOVE =
new ComponentContextAdapter() {
@@ -234,12 +254,12 @@
Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
"m", "h", "s", "n", new ChassisId(0L));
deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device));
- assertEquals("incorrect remove count", 1, providerService.removeCount);
+ assertEquals("incorrect remove count", 2, providerService.removeCount);
device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
"m", "h", "s", "n", new ChassisId(0L));
deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device));
- assertEquals("incorrect remove count", 2, providerService.removeCount);
+ assertEquals("incorrect remove count", 3, providerService.removeCount);
}
@Test
@@ -251,12 +271,12 @@
Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
"m", "h", "s", "n", new ChassisId(0L));
deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device));
- assertEquals("incorrect remove count", 1, providerService.removeCount);
+ assertEquals("incorrect remove count", 2, providerService.removeCount);
device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
"m", "h", "s", "n", new ChassisId(0L));
deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device));
- assertEquals("incorrect remove count", 2, providerService.removeCount);
+ assertEquals("incorrect remove count", 3, providerService.removeCount);
}
@Test
@@ -309,6 +329,43 @@
}
/**
+ * When receiving DHCP REQUEST, update MAC, location of client.
+ * When receiving DHCP ACK, update MAC, location of server and IP of client.
+ */
+ @Test
+ public void testReceiveDhcp() {
+ // DHCP Request
+ testProcessor.process(new TestDhcpRequestPacketContext(DEV1));
+ assertThat("testReceiveDhcpRequest. One host description expected",
+ providerService.descriptions.size(), is(1));
+ // Should learn the MAC and location of DHCP client
+ HostDescription descr = providerService.descriptions.get(0);
+ assertThat(descr.location(), is(LOCATION));
+ assertThat(descr.hwAddress(), is(MAC));
+ assertThat(descr.ipAddress().size(), is(0));
+ assertThat(descr.vlan(), is(VLAN));
+
+ // DHCP Ack
+ testProcessor.process(new TestDhcpAckPacketContext(DEV1));
+ assertThat("testReceiveDhcpAck. 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));
+
+ }
+
+ /**
* When receiving NeighborAdvertisement, updates location and IP.
*/
@Test
@@ -533,7 +590,7 @@
}
/**
- * Generates IPv6 Unicast packet.
+ * Generates IPv4 Unicast packet.
*/
private class TestIpv4PacketContext implements PacketContext {
private final String deviceId;
@@ -589,6 +646,152 @@
return false;
}
}
+ /**
+ * Generates DHCP REQUEST packet.
+ */
+ private class TestDhcpRequestPacketContext implements PacketContext {
+ private final String deviceId;
+
+ public TestDhcpRequestPacketContext(String deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public long time() {
+ return 0;
+ }
+
+ @Override
+ public InboundPacket inPacket() {
+ byte[] dhcpMsgType = new byte[1];
+ dhcpMsgType[0] = (byte) DHCPPacketType.DHCPREQUEST.getValue();
+
+ DHCPOption dhcpOption = new DHCPOption();
+ dhcpOption.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
+ dhcpOption.setData(dhcpMsgType);
+ dhcpOption.setLength((byte) 1);
+ DHCP dhcp = new DHCP();
+ dhcp.setOptions(Collections.singletonList(dhcpOption));
+ dhcp.setClientHardwareAddress(MAC.toBytes());
+ UDP udp = new UDP();
+ udp.setPayload(dhcp);
+ udp.setSourcePort(UDP.DHCP_CLIENT_PORT);
+ udp.setDestinationPort(UDP.DHCP_SERVER_PORT);
+ IPv4 ipv4 = new IPv4();
+ ipv4.setPayload(udp);
+ ipv4.setDestinationAddress(IP_ADDRESS3.toString());
+ ipv4.setSourceAddress(IP_ADDRESS.toString());
+ Ethernet eth = new Ethernet();
+ eth.setEtherType(Ethernet.TYPE_IPV4)
+ .setVlanID(VLAN.toShort())
+ .setSourceMACAddress(MAC)
+ .setDestinationMACAddress(MAC3)
+ .setPayload(ipv4);
+ ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+ portNumber(INPORT));
+ return new DefaultInboundPacket(receivedFrom, eth,
+ ByteBuffer.wrap(eth.serialize()));
+ }
+
+ @Override
+ public OutboundPacket outPacket() {
+ return null;
+ }
+
+ @Override
+ public TrafficTreatment.Builder treatmentBuilder() {
+ return null;
+ }
+
+ @Override
+ public void send() {
+
+ }
+
+ @Override
+ public boolean block() {
+ return false;
+ }
+
+ @Override
+ public boolean isHandled() {
+ return false;
+ }
+ }
+
+ /**
+ * Generates DHCP ACK packet.
+ */
+ private class TestDhcpAckPacketContext implements PacketContext {
+ private final String deviceId;
+
+ public TestDhcpAckPacketContext(String deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public long time() {
+ return 0;
+ }
+
+ @Override
+ public InboundPacket inPacket() {
+ byte[] dhcpMsgType = new byte[1];
+ dhcpMsgType[0] = (byte) DHCPPacketType.DHCPACK.getValue();
+
+ DHCPOption dhcpOption = new DHCPOption();
+ dhcpOption.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
+ dhcpOption.setData(dhcpMsgType);
+ dhcpOption.setLength((byte) 1);
+ DHCP dhcp = new DHCP();
+ dhcp.setOptions(Collections.singletonList(dhcpOption));
+ dhcp.setClientHardwareAddress(MAC.toBytes());
+ dhcp.setYourIPAddress(IP_ADDRESS.getIp4Address().toInt());
+ UDP udp = new UDP();
+ udp.setPayload(dhcp);
+ udp.setSourcePort(UDP.DHCP_SERVER_PORT);
+ udp.setDestinationPort(UDP.DHCP_CLIENT_PORT);
+ IPv4 ipv4 = new IPv4();
+ ipv4.setPayload(udp);
+ ipv4.setDestinationAddress(IP_ADDRESS.toString());
+ ipv4.setSourceAddress(IP_ADDRESS3.toString());
+ Ethernet eth = new Ethernet();
+ eth.setEtherType(Ethernet.TYPE_IPV4)
+ .setVlanID(VLAN.toShort())
+ .setSourceMACAddress(MAC3)
+ .setDestinationMACAddress(MAC)
+ .setPayload(ipv4);
+ ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+ portNumber(INPORT2));
+ return new DefaultInboundPacket(receivedFrom, eth,
+ ByteBuffer.wrap(eth.serialize()));
+ }
+
+ @Override
+ public OutboundPacket outPacket() {
+ return null;
+ }
+
+ @Override
+ public TrafficTreatment.Builder treatmentBuilder() {
+ return null;
+ }
+
+ @Override
+ public void send() {
+
+ }
+
+ @Override
+ public boolean block() {
+ return false;
+ }
+
+ @Override
+ public boolean isHandled() {
+ return false;
+ }
+ }
/**
* Generates NeighborAdvertisement packet.
@@ -1035,10 +1238,13 @@
public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
ConnectPoint cp1 = new ConnectPoint(deviceId(DEV1), portNumber(INPORT));
ConnectPoint cp2 = new ConnectPoint(deviceId(DEV4), portNumber(INPORT));
+ ConnectPoint cp3 = new ConnectPoint(deviceId(DEV1), portNumber(INPORT2));
if (connectPoint.equals(cp1)) {
return ImmutableSet.of(HOST);
} else if (connectPoint.equals(cp2)) {
return ImmutableSet.of(HOST2);
+ } else if (connectPoint.equals(cp3)) {
+ return ImmutableSet.of(HOST3);
} else {
return ImmutableSet.of();
}
@@ -1047,7 +1253,7 @@
@Override
public Set<Host> getConnectedHosts(DeviceId deviceId) {
if (deviceId.equals(deviceId(DEV1))) {
- return ImmutableSet.of(HOST);
+ return ImmutableSet.of(HOST, HOST3);
} else if (deviceId.equals(deviceId(DEV4))) {
return ImmutableSet.of(HOST2);
} else {
@@ -1055,5 +1261,17 @@
}
}
+ @Override
+ public Host getHost(HostId hostId) {
+ if (hostId.equals(HostId.hostId(MAC, VLAN))) {
+ return HOST;
+ } else if (hostId.equals(HostId.hostId(MAC2, VLAN))) {
+ return HOST2;
+ } else if (hostId.equals(HostId.hostId(MAC3, VLAN))) {
+ return HOST3;
+ }
+ return null;
+ }
+
}
}