[CORD-2226] Dhcp6 Relay uses to store dhcp record for LeaseQuery

Change-Id: Ib3baadb38e3f5f6ebe6efc884660fe0c77cfe689
This commit is contained in:
Kalhee Kim 2017-11-09 14:38:37 +00:00 committed by Charles Chan
parent 8e8ff057aa
commit ea4b6c2649
4 changed files with 330 additions and 74 deletions

View File

@ -48,6 +48,8 @@ import org.onlab.packet.dhcp.Dhcp6IaTaOption;
import org.onlab.packet.dhcp.Dhcp6IaPdOption;
import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
import org.onlab.packet.dhcp.Dhcp6Duid;
import org.onlab.util.HexString;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
@ -55,6 +57,7 @@ import org.onosproject.dhcprelay.api.DhcpHandler;
import org.onosproject.dhcprelay.api.DhcpServerInfo;
import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
import org.onosproject.dhcprelay.store.DhcpRelayStore;
import org.onosproject.dhcprelay.store.DhcpRecord;
import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
@ -613,6 +616,35 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
return prefixPrefix;
}
/**
* extract from dhcp6 packet ClientIdOption.
*
* @param directConnFlag directly connected host
* @param dhcp6Payload the dhcp6 payload
* @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
*/
private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
Dhcp6ClientIdOption clientIdOption;
if (directConnFlag) {
clientIdOption = dhcp6Payload.getOptions()
.stream()
.filter(opt -> opt instanceof Dhcp6ClientIdOption)
.map(opt -> (Dhcp6ClientIdOption) opt)
.findFirst()
.orElse(null);
} else {
DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Payload);
clientIdOption = leafDhcp.getOptions()
.stream()
.filter(opt -> opt instanceof Dhcp6ClientIdOption)
.map(opt -> (Dhcp6ClientIdOption) opt)
.findFirst()
.orElse(null);
}
return clientIdOption;
}
/**
* remove host or route.
*
@ -622,55 +654,65 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
* @param clientIpv6 client's Ipv6 packet
* @param clientInterface client interfaces
*/
private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
DHCP6 dhcp6Packet,
Ethernet clientPacket, IPv6 clientIpv6,
Interface clientInterface) {
log.debug("extractPrefix enters {}", dhcp6Packet);
VlanId vlanId = clientInterface.vlan();
MacAddress clientMac = clientPacket.getSourceMAC();
log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
MacAddress gwMac = clientPacket.getSourceMAC(); // could be gw or host
MacAddress leafClientMac;
byte leafMsgType;
log.debug("client mac {} client vlan {}", HexString.toHexString(gwMac.toBytes(), ":"), vlanId);
// add host or route
if (isDhcp6Release(dhcp6Packet)) {
IpAddress ip = null;
if (directConnFlag) {
// Add to host store if it is connected to network directly
ip = extractIpAddress(dhcp6Packet);
if (ip != null) {
HostId hostId = HostId.hostId(clientMac, vlanId);
boolean isMsgRelease = isDhcp6Release(dhcp6Packet);
IpAddress ip;
IpPrefix ipPrefix = null;
if (directConnFlag) {
// Add to host store if it is connected to network directly
ip = extractIpAddress(dhcp6Packet);
if (ip != null) {
if (isMsgRelease) {
HostId hostId = HostId.hostId(gwMac, vlanId);
log.debug("remove Host {} ip for directly connected.", hostId.toString());
// Remove host's ip of when dhcp release msg is received
providerService.removeIpFromHost(hostId, ip);
} else {
log.debug("ipAddress not found. Do not add Host for directly connected.");
}
} else {
// Remove from route store if it is not connected to network directly
// pick out the first link-local ip address
IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
if (nextHopIp == null) {
log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
clientMac, vlanId);
return;
}
log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
HostId.hostId(gwMac, vlanId).toString());
}
leafMsgType = dhcp6Packet.getMsgType();
} else {
// Remove from route store if it is not connected to network directly
// pick out the first link-local ip address
IpAddress nextHopIp = getFirstIpByHost(gwMac, vlanId);
if (nextHopIp == null) {
log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
gwMac, vlanId);
return;
}
DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
ip = extractIpAddress(leafDhcp);
if (ip == null) {
log.debug("ip is null");
} else {
DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
ip = extractIpAddress(leafDhcp);
if (ip == null) {
log.debug("ip is null");
} else {
if (isMsgRelease) {
Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
log.debug("removing route of 128 address for indirectly connected.");
log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
HexString.toHexString(nextHopIp.toOctets(), ":"));
routeStore.removeRoute(routeForIP);
}
}
IpPrefix ipPrefix = extractPrefix(leafDhcp);
if (ipPrefix == null) {
log.debug("ipPrefix is null ");
} else {
ipPrefix = extractPrefix(leafDhcp);
if (ipPrefix == null) {
log.debug("ipPrefix is null ");
} else {
if (isMsgRelease) {
Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
log.debug("removing route of PD for indirectly connected.");
log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
@ -682,38 +724,102 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
}
}
}
leafMsgType = leafDhcp.getMsgType();
}
Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
if (clientIdOption != null) {
log.warn("CLIENTID option found {}", clientIdOption);
if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
(clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
} else {
log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
return;
}
} else {
log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
return;
}
HostId hostId = HostId.hostId(leafClientMac, vlanId);
DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
if (leafMsgType == DHCP6.MsgType.RELEASE.value()) {
log.debug("DHCP6 RELEASE msg.");
if (record != null) {
if (ip != null) {
log.warn("DhcpRelay Record ip6Address is set to null.");
record.ip6Address(null);
}
if (ipPrefix != null) {
log.warn("DhcpRelay Record pdPrefix is set to null.");
record.pdPrefix(null);
}
log.debug("ip {} pd {}", record.ip6Address(), record.pdPrefix());
if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
log.warn("IP6 address and IP6 PD both are null. Remove record.");
dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
}
}
return;
}
if (record == null) {
record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
} else {
record = record.clone();
}
record.addLocation(new HostLocation(location, System.currentTimeMillis()));
record.ip6Status(DHCP6.MsgType.getType(dhcp6Packet.getMsgType()));
record.setDirectlyConnected(directConnFlag);
if (!directConnFlag) {
// Update gateway mac address if the host is not directly connected
record.nextHop(gwMac);
}
record.updateLastSeen();
dhcpRelayStore.updateDhcpRecord(HostId.hostId(leafClientMac, vlanId), record);
}
/**
* add host or route.
*
* @param directConnFlag flag to show that packet is from directly connected client
* @param location client side connect point
* @param dhcp6Relay the dhcp6 payload
* @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
* @param clientMac client macAddress
* @param embeddedDhcp6 the dhcp6 payload within relay
* @param gwMac client gw/host macAddress
* @param clientInterface client interface
*/
private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
DHCP6 embeddedDhcp6,
MacAddress clientMac,
Interface clientInterface) {
private void addHostOrRoute(boolean directConnFlag,
ConnectPoint location,
DHCP6 dhcp6Relay,
DHCP6 embeddedDhcp6,
MacAddress gwMac,
Interface clientInterface) {
log.debug("addHostOrRoute entered.");
VlanId vlanId = clientInterface.vlan();
Boolean isMsgReply = isDhcp6Reply(dhcp6Relay);
MacAddress leafClientMac;
Byte leafMsgType;
// add host or route
if (isDhcp6Reply(dhcp6Relay)) {
IpAddress ip = null;
if (directConnFlag) {
// Add to host store if it connect to network directly
ip = extractIpAddress(embeddedDhcp6);
if (ip != null) {
IpAddress ip;
IpPrefix ipPrefix = null;
if (directConnFlag) {
// Add to host store if it connect to network directly
ip = extractIpAddress(embeddedDhcp6);
if (ip != null) {
if (isMsgReply) {
Set<IpAddress> ips = Sets.newHashSet(ip);
// FIXME: we should use vlan id from original packet (solicit, request)
HostId hostId = HostId.hostId(clientMac, vlanId);
HostId hostId = HostId.hostId(gwMac, vlanId);
Host host = hostService.getHost(hostId);
HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
System.currentTimeMillis());
System.currentTimeMillis());
Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
if (host != null) {
@ -721,43 +827,49 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
// if host exists, use old locations and new location
hostLocations.addAll(host.locations());
}
HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
hostLocations, ips,
false);
HostDescription desc = new DefaultHostDescription(gwMac, vlanId,
hostLocations, ips,
false);
log.debug("adding Host for directly connected.");
log.debug("client mac {} client vlan {} hostlocation {}",
HexString.toHexString(clientMac.toBytes(), ":"),
HexString.toHexString(gwMac.toBytes(), ":"),
vlanId, hostLocation.toString());
// Replace the ip when dhcp server give the host new ip address
providerService.hostDetected(hostId, desc, false);
} else {
log.debug("ipAddress not found. Do not add Host for directly connected.");
}
} else {
// Add to route store if it does not connect to network directly
// pick out the first link-local ip address
IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
if (nextHopIp == null) {
log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
clientMac, vlanId);
return;
}
log.warn("ipAddress not found. Do not add Host {} for directly connected.",
HostId.hostId(gwMac, vlanId).toString());
}
leafMsgType = embeddedDhcp6.getMsgType();
} else {
// Add to route store if it does not connect to network directly
// pick out the first link-local ip address
IpAddress nextHopIp = getFirstIpByHost(gwMac, vlanId);
if (nextHopIp == null) {
log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
gwMac, vlanId);
return;
}
DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
ip = extractIpAddress(leafDhcp);
if (ip == null) {
DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
ip = extractIpAddress(leafDhcp);
if (ip == null) {
log.debug("ip is null");
} else {
} else {
if (isMsgReply) {
Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
log.debug("adding Route of 128 address for indirectly connected.");
routeStore.updateRoute(routeForIP);
}
}
IpPrefix ipPrefix = extractPrefix(leafDhcp);
if (ipPrefix == null) {
ipPrefix = extractPrefix(leafDhcp);
if (ipPrefix == null) {
log.debug("ipPrefix is null ");
} else {
} else {
if (isMsgReply) {
Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
log.debug("adding Route of PD for indirectly connected.");
routeStore.updateRoute(routeForPrefix);
@ -767,7 +879,60 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
}
}
}
leafMsgType = leafDhcp.getMsgType();
}
Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
if (clientIdOption != null) {
log.debug("CLIENTID option found {}", clientIdOption);
if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
(clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
} else {
log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
return;
}
} else {
log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
return;
}
if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
(leafMsgType == DHCP6.MsgType.REPLY.value()) && ip == null) {
log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", DHCP6.MsgType.getType(leafMsgType));
return;
}
HostId hostId = HostId.hostId(leafClientMac, vlanId);
DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
if (record == null) {
record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
} else {
record = record.clone();
}
record.addLocation(new HostLocation(location, System.currentTimeMillis()));
if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
if (ip != null) {
log.debug("IP6 address is being stored into dhcp-relay store.");
log.debug("IP6 address {}", HexString.toHexString(ip.toOctets(), ":"));
record.ip6Address(ip.getIp6Address());
} else {
log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
}
if (ipPrefix != null) {
log.debug("IP6 PD address is being stored into dhcp-relay store.");
log.debug("IP6 PD address {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"));
record.pdPrefix(ipPrefix);
} else {
log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
}
}
record.ip6Status(DHCP6.MsgType.getType(dhcp6Relay.getMsgType()));
record.setDirectlyConnected(directConnFlag);
record.updateLastSeen();
dhcpRelayStore.updateDhcpRecord(HostId.hostId(leafClientMac, vlanId), record);
}
/**
@ -878,7 +1043,8 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
.stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
.findFirst().orElse(null);
removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
removeHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Packet, clientPacket,
clientIpv6, clientInterface);
DHCP6 dhcp6Relay = new DHCP6();
dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
@ -905,6 +1071,8 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
}
if (indirectRelayAgentIpFromCfg == null) {
dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
@ -916,6 +1084,7 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
}
}
// peer address: address of the client or relay agent from which
// the message to be relayed was received.
dhcp6Relay.setPeerAddress(peerAddress);
@ -1111,7 +1280,7 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
// add host or route
addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
udpPacket.setPayload(embeddedDhcp6);
udpPacket.resetChecksum();
@ -1581,4 +1750,5 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
.orElse(null);
return nextHopIp;
}
}

View File

@ -49,6 +49,8 @@ public class DhcpRelayCommand extends AbstractShellCommand {
private static final String NA = "N/A";
private static final String STATUS_FMT = "[%s, %s]";
private static final String STATUS_FMT_NH = "[%s via %s, %s]";
private static final String STATUS_FMT_V6 = "[%s, %s, %s]";
private static final String STATUS_FMT_V6_NH = "[%s, %s via %s, %s]";
private static final String DEFAULT_SERVERS = "Default DHCP servers:";
private static final String INDIRECT_SERVERS = "Indirect DHCP servers:";
@ -135,13 +137,22 @@ public class DhcpRelayCommand extends AbstractShellCommand {
}
private String ip6State(DhcpRecord record) {
String nextHopIp = findNextHopIp(IpAddress::isIp6,
String nextHopIp = findNextHopIp6(IpAddress::isIp6,
record.nextHop().orElse(null),
record.vlanId());
return ipState(record.ip6Address().map(Object::toString).orElse(NA),
record.ip6Status().map(Object::toString).orElse(NA),
record.directlyConnected(),
nextHopIp);
if (record.directlyConnected()) {
return String.format(STATUS_FMT_V6,
record.ip6Address().map(Object::toString).orElse(NA),
record.pdPrefix().map(Object::toString).orElse(NA),
record.ip6Status().map(Object::toString).orElse(NA));
} else {
return String.format(STATUS_FMT_V6_NH,
record.ip6Address().map(Object::toString).orElse(NA),
record.pdPrefix().map(Object::toString).orElse(NA),
nextHopIp,
record.ip6Status().map(Object::toString).orElse(NA));
}
}
private String ipState(String ipAddress, String status,
@ -158,6 +169,7 @@ public class DhcpRelayCommand extends AbstractShellCommand {
if (ipFilter == null || nextHopMac == null || vlanId == null) {
return NA;
}
Host host = HOST_SERVICE.getHost(HostId.hostId(nextHopMac, vlanId));
if (host == null) {
return NA;
@ -169,4 +181,21 @@ public class DhcpRelayCommand extends AbstractShellCommand {
.findFirst()
.orElse(NA);
}
private String findNextHopIp6(Predicate<IpAddress> ipFilter, MacAddress nextHopMac, VlanId vlanId) {
if (ipFilter == null || nextHopMac == null || vlanId == null) {
return NA;
}
Host host = HOST_SERVICE.getHost(HostId.hostId(nextHopMac, vlanId));
if (host == null) {
return NA;
}
return host.ipAddresses().stream()
.filter(ipFilter)
.filter(ip -> ip.isLinkLocal())
.map(Object::toString)
.findFirst()
.orElse(NA);
}
}

View File

@ -22,6 +22,7 @@ import org.onlab.packet.DHCP;
import org.onlab.packet.DHCP6;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.HostId;
@ -49,6 +50,7 @@ public class DhcpRecord {
private DHCP.MsgType ip4Status;
private Ip6Address ip6Address;
private IpPrefix pdPrefix;
private DHCP6.MsgType ip6Status;
private long lastSeen;
@ -158,6 +160,26 @@ public class DhcpRecord {
return this;
}
/**
* Gets IPv6 PD address which assigned to the host.
*
* @return the PD IP address assigned to the host
*/
public Optional<IpPrefix> pdPrefix() {
return Optional.ofNullable(pdPrefix);
}
/**
* Sets IPv6 PD address.
*
* @param pdPrefix the IPv6 PD address
* @return the DHCP record
*/
public DhcpRecord pdPrefix(IpPrefix pdPrefix) {
this.pdPrefix = pdPrefix;
return this;
}
/**
* Gets the latest time this record updated.
*
@ -292,6 +314,7 @@ public class DhcpRecord {
newRecord.ip4Address = ip4Address;
newRecord.ip4Status = ip4Status;
newRecord.ip6Address = ip6Address;
newRecord.pdPrefix = pdPrefix;
newRecord.ip6Status = ip6Status;
newRecord.lastSeen = lastSeen;
return newRecord;
@ -300,7 +323,7 @@ public class DhcpRecord {
@Override
public int hashCode() {
return Objects.hash(locations, macAddress, vlanId, ip4Address, ip4Status,
nextHop, nextHopTemp, ip6Address, ip6Status, lastSeen);
nextHop, nextHopTemp, ip6Address, pdPrefix, ip6Status, lastSeen);
}
@Override
@ -320,6 +343,7 @@ public class DhcpRecord {
Objects.equals(nextHop, that.nextHop) &&
Objects.equals(nextHopTemp, that.nextHopTemp) &&
Objects.equals(ip6Address, that.ip6Address) &&
Objects.equals(pdPrefix, that.pdPrefix) &&
Objects.equals(ip6Status, that.ip6Status) &&
Objects.equals(lastSeen, that.lastSeen) &&
Objects.equals(directlyConnected, that.directlyConnected);
@ -336,6 +360,7 @@ public class DhcpRecord {
.add("nextHop", nextHop)
.add("nextHopTemp", nextHopTemp)
.add("ip6Address", ip6Address)
.add("pdPrefix", pdPrefix)
.add("ip6State", ip6Status)
.add("lastSeen", lastSeen)
.add("directlyConnected", directlyConnected)

View File

@ -79,6 +79,38 @@ public class DHCP6 extends BasePacket {
public byte value() {
return this.value;
}
public static MsgType getType(final int value) {
switch (value) {
case 1:
return SOLICIT;
case 2:
return ADVERTISE;
case 3:
return REQUEST;
case 4:
return CONFIRM;
case 5:
return RENEW;
case 6:
return REBIND;
case 7:
return REPLY;
case 8:
return RELEASE;
case 9:
return DECLINE;
case 10:
return RECONFIGURE;
case 11:
return INFORMATION_REQUEST;
case 12:
return RELAY_FORW;
case 13:
return RELAY_REPL;
default:
return null;
}
}
}
/**