Support for ipv4 dhcp multi server

Change-Id: I77ccf2430f875908423c3d00dbc67517034185fd
(cherry picked from commit 0a856fa12a0e3f1e54898e212f864c207b770671)
This commit is contained in:
rsahot036 2018-02-26 15:01:37 -05:00 committed by Charles Chan
parent 71670d1185
commit 620655b7f8
2 changed files with 578 additions and 268 deletions

View File

@ -94,6 +94,7 @@ import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@ -109,6 +110,8 @@ import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.C
import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
import org.onosproject.dhcprelay.Dhcp4HandlerUtil.InternalPacket;
@Component
@Service
@Property(name = "version", value = "4")
@ -172,6 +175,7 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
private Dhcp4HandlerUtil dhcp4HandlerUtil = new Dhcp4HandlerUtil();
@Activate
protected void activate() {
@ -260,18 +264,22 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
return;
}
// TODO: currently we pick up first DHCP server config.
// Will use other server configs in the future for HA.
DhcpServerConfig serverConfig = configs.iterator().next();
if (!serverConfig.getDhcpServerIp4().isPresent()) {
// not a DHCPv4 config
return;
Boolean isConfigValid = false;
for (DhcpServerConfig serverConfig : configs) {
if (serverConfig.getDhcpServerIp4().isPresent()) {
isConfigValid = true;
break;
}
}
if (!serverInfoList.isEmpty()) {
if (!isConfigValid) {
log.warn("No IP V4 server address found.");
return; // No IP V6 address found
}
// if (!serverInfoList.isEmpty()) {
for (DhcpServerInfo oldServerInfo : serverInfoList) {
log.info("In for (DhcpServerInfo oldServerInfo : serverInfoList) {");
// remove old server info
DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
//DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
// stop monitoring gateway or server
oldServerInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
@ -284,43 +292,48 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
}
// Create new server info according to the config
DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
DhcpServerInfo.Version.DHCP_V4);
checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
"Connect point not exists");
checkState(newServerInfo.getDhcpServerIp4().isPresent(),
"IP of DHCP server not exists");
serverInfoList.clear();
for (DhcpServerConfig serverConfig : configs) {
log.info("// Create new server info according to the config");
DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
DhcpServerInfo.Version.DHCP_V4);
checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
"Connect point not exists");
checkState(newServerInfo.getDhcpServerIp4().isPresent(),
"IP of DHCP server not exists");
log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().orElse(null));
log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().orElse(null));
Ip4Address serverIp = newServerInfo.getDhcpServerIp4().get();
Ip4Address ipToProbe;
if (newServerInfo.getDhcpGatewayIp4().isPresent()) {
ipToProbe = newServerInfo.getDhcpGatewayIp4().get();
} else {
ipToProbe = newServerInfo.getDhcpServerIp4().orElse(null);
Ip4Address serverIp = newServerInfo.getDhcpServerIp4().get();
Ip4Address ipToProbe;
if (newServerInfo.getDhcpGatewayIp4().isPresent()) {
ipToProbe = newServerInfo.getDhcpGatewayIp4().get();
} else {
ipToProbe = newServerInfo.getDhcpServerIp4().orElse(null);
}
log.info("Probe_IP {}", ipToProbe);
String hostToProbe = newServerInfo.getDhcpGatewayIp4()
.map(ip -> "gateway").orElse("server");
log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
hostService.startMonitoringIp(ipToProbe);
Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
if (!hosts.isEmpty()) {
Host host = hosts.iterator().next();
newServerInfo.setDhcpConnectVlan(host.vlan());
newServerInfo.setDhcpConnectMac(host.mac());
}
// Add new server info
synchronized (this) {
//serverInfoList.clear();
serverInfoList.add(newServerInfo);
}
requestDhcpPacket(serverIp);
}
String hostToProbe = newServerInfo.getDhcpGatewayIp4()
.map(ip -> "gateway").orElse("server");
log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
hostService.startMonitoringIp(ipToProbe);
Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
if (!hosts.isEmpty()) {
Host host = hosts.iterator().next();
newServerInfo.setDhcpConnectVlan(host.vlan());
newServerInfo.setDhcpConnectMac(host.mac());
}
// Add new server info
synchronized (this) {
serverInfoList.clear();
serverInfoList.add(0, newServerInfo);
}
requestDhcpPacket(serverIp);
}
@Override
@ -343,20 +356,27 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
.findFirst()
.orElse(null);
checkNotNull(incomingPacketType, "Can't get message type from DHCP payload {}", dhcpPayload);
Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
//ignore the packets if dhcp client interface is not configured on onos.
if (receivingInterfaces.isEmpty()) {
log.warn("Virtual interface is not configured on {}", inPort);
return;
}
switch (incomingPacketType) {
case DHCPDISCOVER:
// 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 ethernetPacketDiscover =
processDhcpPacketFromClient(context, packet);
if (ethernetPacketDiscover != null) {
List<InternalPacket> ethernetClientPacket =
processDhcpPacketFromClient(context, packet, receivingInterfaces);
for (InternalPacket internalPacket : ethernetClientPacket) {
log.debug("DHCPDISCOVER from {} Forward to server", inPort);
writeRequestDhcpRecord(inPort, packet, dhcpPayload);
handleDhcpDiscoverAndRequest(ethernetPacketDiscover, dhcpPayload);
forwardPacket(internalPacket);
}
break;
case DHCPOFFER:
//reply to dhcp client.
Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Ethernet ethernetPacketOffer = processDhcpPacketFromServer(context, packet);
if (ethernetPacketOffer != null) {
writeResponseDhcpRecord(ethernetPacketOffer, dhcpPayload);
sendResponseToClient(ethernetPacketOffer, dhcpPayload);
@ -365,18 +385,19 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
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);
if (ethernetPacketRequest != null) {
List<InternalPacket> ethernetPacketRequest =
processDhcpPacketFromClient(context, packet, receivingInterfaces);
for (InternalPacket internalPacket : ethernetPacketRequest) {
log.debug("DHCPDISCOVER from {} Forward to server", inPort);
writeRequestDhcpRecord(inPort, packet, dhcpPayload);
handleDhcpDiscoverAndRequest(ethernetPacketRequest, dhcpPayload);
forwardPacket(internalPacket);
}
break;
case DHCPDECLINE:
break;
case DHCPACK:
// reply to dhcp client.
Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Ethernet ethernetPacketAck = processDhcpPacketFromServer(context, packet);
if (ethernetPacketAck != null) {
writeResponseDhcpRecord(ethernetPacketAck, dhcpPayload);
handleDhcpAck(ethernetPacketAck, dhcpPayload);
@ -574,9 +595,12 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
// do a basic routing of the packet (this is unicast routing
// not a relay operation like for other broadcast dhcp packets
Ethernet ethernetPacketLQ = processLeaseQueryFromAgent(context, packet);
List<InternalPacket> ethernetPacketRequest = processLeaseQueryFromAgent(context, packet);
// and forward to server
handleDhcpDiscoverAndRequest(ethernetPacketLQ, dhcpPayload);
for (InternalPacket internalPacket : ethernetPacketRequest) {
log.debug("LeaseQueryMsg forward to server");
forwardPacket(internalPacket);
}
} else {
log.warn("LQ: Error! - DHCP relay record for that client not found - ignoring LQ!");
}
@ -645,43 +669,29 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
* @param ethernetPacket the ethernet payload to process
* @return processed packet
*/
private Ethernet processDhcpPacketFromClient(PacketContext context,
Ethernet ethernetPacket) {
private List<InternalPacket> processDhcpPacketFromClient(PacketContext context,
Ethernet ethernetPacket,
Set<Interface> clientInterfaces) {
ConnectPoint receivedFrom = context.inPacket().receivedFrom();
DeviceId receivedFromDevice = receivedFrom.deviceId();
Ip4Address relayAgentIp = null;
relayAgentIp = dhcp4HandlerUtil.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;
}
log.debug("Multi DHCP V4 processDhcpPacketFromClient on port {}",
clientInterfaces.iterator().next().connectPoint());
// get dhcp header.
Ethernet etherReply = ethernetPacket.duplicate();
Ethernet etherReply = (Ethernet) ethernetPacket.clone();
IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
UDP udpPacket = (UDP) ipv4Packet.getPayload();
DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
// TODO: refactor
VlanId dhcpConnectVlan = null;
MacAddress dhcpConnectMac = null;
Ip4Address dhcpServerIp = null;
Ip4Address relayAgentIp = null;
VlanId indirectDhcpConnectVlan = null;
MacAddress indirectDhcpConnectMac = null;
Ip4Address indirectDhcpServerIp = null;
Ip4Address indirectRelayAgentIp = null;
if (!defaultServerInfoList.isEmpty()) {
DhcpServerInfo serverInfo = defaultServerInfoList.get(0);
dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
relayAgentIp = serverInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
}
if (!indirectServerInfoList.isEmpty()) {
DhcpServerInfo indirectServerInfo = indirectServerInfoList.get(0);
indirectDhcpConnectVlan = indirectServerInfo.getDhcpConnectVlan().orElse(null);
indirectDhcpConnectMac = indirectServerInfo.getDhcpConnectMac().orElse(null);
indirectDhcpServerIp = indirectServerInfo.getDhcpServerIp4().orElse(null);
indirectRelayAgentIp = indirectServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
}
Ip4Address clientInterfaceIp =
interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
@ -695,112 +705,149 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
.orElse(null);
if (clientInterfaceIp == null) {
log.warn("Can't find interface IP for client interface for port {}",
context.inPacket().receivedFrom());
context.inPacket().receivedFrom());
return null;
}
boolean isDirectlyConnected = directlyConnected(dhcpPacket);
Interface serverInterface;
if (isDirectlyConnected) {
serverInterface = getDefaultServerInterface();
} else {
serverInterface = getIndirectServerInterface();
boolean directConnFlag = directlyConnected(dhcpPacket);
// Multi DHCP Start
ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
.stream().filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
.findFirst()
.orElse(null);
List<InternalPacket> internalPackets = new ArrayList<>();
List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
for (DhcpServerInfo serverInfo : copyServerInfoList) {
etherReply = (Ethernet) ethernetPacket.clone();
ipv4Packet = (IPv4) etherReply.getPayload();
udpPacket = (UDP) ipv4Packet.getPayload();
dhcpPacket = (DHCP) udpPacket.getPayload();
if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
log.warn("Can't get server connect point, ignore");
continue;
}
DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
if (newServerInfo == null) {
log.warn("Can't get server interface with host info resolved, ignore");
continue;
}
Interface serverInterface = getServerInterface(newServerInfo);
if (serverInterface == null) {
// Indirect server interface not found, use default server interface
serverInterface = getDefaultServerInterface();
log.warn("Can't get server interface, ignore");
continue;
}
}
if (serverInterface == null) {
log.warn("Can't get {} server interface, ignore", isDirectlyConnected ? "direct" : "indirect");
return null;
}
Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
MacAddress macFacingServer = serverInterface.mac();
if (ipFacingServer == null || macFacingServer == null) {
log.warn("No IP address for server Interface {}", serverInterface);
return null;
}
if (dhcpConnectMac == null) {
log.warn("DHCP Server/Gateway IP not yet resolved .. Aborting DHCP "
+ "packet processing from client on port: {}",
context.inPacket().receivedFrom());
return null;
}
etherReply.setSourceMACAddress(macFacingServer);
ipv4Packet.setSourceAddress(ipFacingServer.toInt());
Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
MacAddress macFacingServer = serverInterface.mac();
log.debug("Interfacing server {} Mac : {} ", ipFacingServer, macFacingServer);
if (ipFacingServer == null || macFacingServer == null) {
log.warn("No IP address for server Interface {}", serverInterface);
//return null;
continue;
}
if (isDirectlyConnected) {
etherReply.setDestinationMACAddress(dhcpConnectMac);
etherReply.setVlanID(dhcpConnectVlan.toShort());
ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
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);
etherReply.setSourceMACAddress(macFacingServer);
// set default info and replace with indirect if available later on.
if (newServerInfo.getDhcpConnectMac().isPresent()) {
etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
}
if (newServerInfo.getDhcpConnectVlan().isPresent()) {
etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
}
ipv4Packet.setSourceAddress(ipFacingServer.toInt());
ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
log.info("Directly connected {}", isDirectlyConnected);
log.info("Dhcp Server IP: {}", newServerInfo.getDhcpServerIp4().get());
if (isDirectlyConnected) {
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);
// Sets relay agent IP
int effectiveRelayAgentIp = relayAgentIp != null ?
relayAgentIp.toInt() : clientInterfaceIp.toInt();
dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
} else {
if (indirectDhcpServerIp != null) {
// Use indirect server config for indirect packets if configured
etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
ipv4Packet.setDestinationAddress(indirectDhcpServerIp.toInt());
// Set giaddr if indirect relay agent IP is configured
if (indirectRelayAgentIp != null) {
dhcpPacket.setGatewayIPAddress(indirectRelayAgentIp.toInt());
log.info("**Default****Dhcp Server IP: {}", newServerInfo.getDhcpServerIp4().get());
if (newServerInfo.getDhcpConnectMac().isPresent()) {
etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
}
if (newServerInfo.getDhcpConnectVlan().isPresent()) {
etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
}
ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
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);
relayAgentIp = serverInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
// Sets relay agent IP
int effectiveRelayAgentIp = relayAgentIp != null ?
relayAgentIp.toInt() : clientInterfaceIp.toInt();
dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
log.info("In Default, Relay Agent IP {}", effectiveRelayAgentIp);
} else {
// Otherwise, use default server config for indirect packets
etherReply.setDestinationMACAddress(dhcpConnectMac);
etherReply.setVlanID(dhcpConnectVlan.toShort());
ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
// Set giaddr if direct relay agent IP is configured
if (relayAgentIp != null) {
dhcpPacket.setGatewayIPAddress(relayAgentIp.toInt());
if (!newServerInfo.getDhcpServerIp4().isPresent()) {
// do nothing
} else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
continue;
} else {
relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
// Sets relay agent IP
int effectiveRelayAgentIp = relayAgentIp != null ?
relayAgentIp.toInt() : clientInterfaceIp.toInt();
dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
}
}
}
udpPacket.setPayload(dhcpPacket);
// As a DHCP relay, the source port should be server port( instead
// of client port.
udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
ipv4Packet.setPayload(udpPacket);
ipv4Packet.setTtl((byte) 64);
etherReply.setPayload(ipv4Packet);
return etherReply;
// Remove broadcast flag
dhcpPacket.setFlags((short) 0);
udpPacket.setPayload(dhcpPacket);
// As a DHCP relay, the source port should be server port( instead
// of client port.
udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
ipv4Packet.setPayload(udpPacket);
ipv4Packet.setTtl((byte) 64);
etherReply.setPayload(ipv4Packet);
InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
serverInfo.getDhcpServerConnectPoint().get());
internalPackets.add(internalPacket);
}
return internalPackets;
}
@ -811,14 +858,20 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
* @param ethernetPacket the ethernet payload to process
* @return processed packet
*/
private Ethernet processLeaseQueryFromAgent(PacketContext context,
Ethernet ethernetPacket) {
private List<InternalPacket> processLeaseQueryFromAgent(PacketContext context,
Ethernet ethernetPacket) {
ConnectPoint receivedFrom = context.inPacket().receivedFrom();
DeviceId receivedFromDevice = receivedFrom.deviceId();
// get dhcp header.
Ethernet etherReply = (Ethernet) ethernetPacket.clone();
IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
UDP udpPacket = (UDP) ipv4Packet.getPayload();
DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
Ip4Address relayAgentIp = null;
VlanId dhcpConnectVlan = null;
MacAddress dhcpConnectMac = null;
Ip4Address dhcpServerIp = null;
@ -827,20 +880,6 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
MacAddress indirectDhcpConnectMac = null;
Ip4Address indirectDhcpServerIp = null;
if (!defaultServerInfoList.isEmpty()) {
DhcpServerInfo serverInfo = defaultServerInfoList.get(0);
dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
}
if (!indirectServerInfoList.isEmpty()) {
DhcpServerInfo indirectServerInfo = indirectServerInfoList.get(0);
indirectDhcpConnectVlan = indirectServerInfo.getDhcpConnectVlan().orElse(null);
indirectDhcpConnectMac = indirectServerInfo.getDhcpConnectMac().orElse(null);
indirectDhcpServerIp = indirectServerInfo.getDhcpServerIp4().orElse(null);
}
Ip4Address clientInterfaceIp =
interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
.stream()
@ -853,58 +892,109 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
.orElse(null);
if (clientInterfaceIp == null) {
log.warn("Can't find interface IP for client interface for port {}",
context.inPacket().receivedFrom());
context.inPacket().receivedFrom());
return null;
}
boolean isDirectlyConnected = directlyConnected(dhcpPacket);
Interface serverInterface;
if (isDirectlyConnected) {
serverInterface = getDefaultServerInterface();
} else {
serverInterface = getIndirectServerInterface();
if (serverInterface == null) {
// Indirect server interface not found, use default server interface
serverInterface = getDefaultServerInterface();
boolean directConnFlag = directlyConnected(dhcpPacket);
// Multi DHCP Start
ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
.stream().filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
.findFirst()
.orElse(null);
List<InternalPacket> internalPackets = new ArrayList<>();
List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
for (DhcpServerInfo serverInfo : copyServerInfoList) {
// get dhcp header.
etherReply = (Ethernet) ethernetPacket.clone();
ipv4Packet = (IPv4) etherReply.getPayload();
udpPacket = (UDP) ipv4Packet.getPayload();
dhcpPacket = (DHCP) udpPacket.getPayload();
if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
log.warn("Can't get server connect point, ignore");
continue;
}
DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
if (newServerInfo == null) {
log.warn("Can't get server interface with host info resolved, ignore");
continue;
}
}
if (serverInterface == null) {
log.warn("Can't get {} server interface, ignore", isDirectlyConnected ? "direct" : "indirect");
return null;
}
Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
MacAddress macFacingServer = serverInterface.mac();
if (ipFacingServer == null || macFacingServer == null) {
log.warn("No IP address for server Interface {}", serverInterface);
return null;
}
if (dhcpConnectMac == null) {
log.warn("DHCP server/gateway not yet resolved .. Aborting DHCP "
+ "packet processing from client on port: {}",
context.inPacket().receivedFrom());
return null;
}
etherReply.setSourceMACAddress(macFacingServer);
etherReply.setDestinationMACAddress(dhcpConnectMac);
etherReply.setVlanID(dhcpConnectVlan.toShort());
ipv4Packet.setSourceAddress(ipFacingServer.toInt());
ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
Interface serverInterface = getServerInterface(newServerInfo);
if (serverInterface == null) {
log.warn("Can't get server interface, ignore");
continue;
}
Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
MacAddress macFacingServer = serverInterface.mac();
if (ipFacingServer == null || macFacingServer == null) {
log.warn("No IP address for server Interface {}", serverInterface);
continue;
}
if (indirectDhcpServerIp != null) {
// Indirect case, replace destination to indirect dhcp server if exist
etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
ipv4Packet.setDestinationAddress(indirectDhcpServerIp.toInt());
etherReply.setSourceMACAddress(macFacingServer);
etherReply.setDestinationMACAddress(dhcpConnectMac);
etherReply.setVlanID(dhcpConnectVlan.toShort());
ipv4Packet.setSourceAddress(ipFacingServer.toInt());
ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
if (isDirectlyConnected) {
// set default info and replace with indirect if available later on.
if (newServerInfo.getDhcpConnectMac().isPresent()) {
etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
}
if (newServerInfo.getDhcpConnectVlan().isPresent()) {
etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
}
relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
// Sets relay agent IP
int effectiveRelayAgentIp = relayAgentIp != null ?
relayAgentIp.toInt() : clientInterfaceIp.toInt();
dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
} else {
if (!newServerInfo.getDhcpServerIp4().isPresent()) {
//do nothing
} else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
continue;
} else {
relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
// Sets relay agent IP
int effectiveRelayAgentIp = relayAgentIp != null ?
relayAgentIp.toInt() : clientInterfaceIp.toInt();
dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
log.info("Relay Agent IP {}", relayAgentIp);
}
log.info("In Direct");
}
// Remove broadcast flag
dhcpPacket.setFlags((short) 0);
udpPacket.setPayload(dhcpPacket);
// As a DHCP relay, the source port should be server port( instead
// of client port.
udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
ipv4Packet.setPayload(udpPacket);
ipv4Packet.setTtl((byte) 64);
etherReply.setPayload(ipv4Packet);
////return etherReply;
Dhcp4HandlerUtil.InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
newServerInfo.getDhcpServerConnectPoint().get());
internalPackets.add(internalPacket);
}
log.warn("num of processLeaseQueryFromAgent packets to send is{}", internalPackets.size());
udpPacket.setPayload(dhcpPacket);
// As a DHCP relay, the source port should be server port( instead
// of client port.
udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
ipv4Packet.setPayload(udpPacket);
etherReply.setPayload(ipv4Packet);
return etherReply;
return internalPackets;
}
@ -982,9 +1072,9 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
* @param ethernetPacket the original packet comes from server
* @return new packet which will send to the client
*/
private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
private Ethernet processDhcpPacketFromServer(PacketContext context, Ethernet ethernetPacket) {
// get dhcp header.
Ethernet etherReply = ethernetPacket.duplicate();
Ethernet etherReply = (Ethernet) ethernetPacket.clone();
IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
UDP udpPacket = (UDP) ipv4Packet.getPayload();
DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
@ -998,6 +1088,19 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
return null;
}
VlanId vlanId;
ConnectPoint inPort = context.inPacket().receivedFrom();
boolean directConnFlag = directlyConnected(dhcpPayload);
DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
if (foundServerInfo == null) {
log.warn("Cannot find server info");
return null;
} else {
if (dhcp4HandlerUtil.isServerIpEmpty(foundServerInfo)) {
log.warn("Cannot find server info's ipaddress");
return null;
}
}
if (clientInterface.vlanTagged().isEmpty()) {
vlanId = clientInterface.vlan();
} else {
@ -1158,7 +1261,7 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
* @return Ethernet packet processed
*/
private Ethernet removeRelayAgentOption(Ethernet ethPacket) {
Ethernet ethernet = ethPacket.duplicate();
Ethernet ethernet = (Ethernet) ethPacket.duplicate();
IPv4 ipv4 = (IPv4) ethernet.getPayload();
UDP udp = (UDP) ipv4.getPayload();
DHCP dhcpPayload = (DHCP) udp.getPayload();
@ -1287,34 +1390,6 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
}
}
/**
* forward the packet to ConnectPoint where the DHCP server is attached.
*
* @param packet the packet
*/
private void handleDhcpDiscoverAndRequest(Ethernet packet, DHCP dhcpPayload) {
boolean direct = directlyConnected(dhcpPayload);
DhcpServerInfo serverInfo = defaultServerInfoList.get(0);
if (!direct && !indirectServerInfoList.isEmpty()) {
serverInfo = indirectServerInfoList.get(0);
}
ConnectPoint portToFotward = serverInfo.getDhcpServerConnectPoint().orElse(null);
// send packet to dhcp server connect point.
if (portToFotward != null) {
TrafficTreatment t = DefaultTrafficTreatment.builder()
.setOutput(portToFotward.port()).build();
OutboundPacket o = new DefaultOutboundPacket(
portToFotward.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
@ -1641,4 +1716,135 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
public void setDhcpFpmEnabled(Boolean enabled) {
// v4 does not use fpm. Do nothing.
}
private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
List<DhcpServerInfo> validServerInfo;
if (directConnFlag || indirectServerInfoList.isEmpty()) {
validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
} else {
validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
}
return validServerInfo;
}
private boolean checkDhcpServerConnPt(boolean directConnFlag,
DhcpServerInfo serverInfo) {
if (serverInfo.getDhcpServerConnectPoint() == null) {
log.warn("DHCP4 server connect point for {} connPt {}",
directConnFlag ? "direct" : "indirect", serverInfo.getDhcpServerConnectPoint());
return false;
}
return true;
}
/**
* Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
*
* @param serverInfo server information
* @return newServerInfo if host info can be either found or filled in.
*/
private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
DhcpServerInfo newServerInfo = null;
MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
newServerInfo = serverInfo;
log.warn("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp4(),
dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
} else {
log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp4(),
dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
Ip4Address ipToProbe;
if (serverInfo.getDhcpGatewayIp4().isPresent()) {
ipToProbe = serverInfo.getDhcpGatewayIp4().get();
} else {
ipToProbe = serverInfo.getDhcpServerIp4().orElse(null);
}
String hostToProbe = serverInfo.getDhcpGatewayIp6()
.map(ip -> "gateway").orElse("server");
log.warn("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
hostService.startMonitoringIp(ipToProbe);
Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
if (!hosts.isEmpty()) {
int serverInfoIndex = sererInfoList.indexOf(serverInfo);
Host host = hosts.iterator().next();
serverInfo.setDhcpConnectVlan(host.vlan());
serverInfo.setDhcpConnectMac(host.mac());
// replace the serverInfo in the list
sererInfoList.set(serverInfoIndex, serverInfo);
newServerInfo = serverInfo;
log.warn("Dynamically host found host {}", host);
} else {
log.warn("No host found host ip {} dynamically", ipToProbe);
}
}
return newServerInfo;
}
/**
* Gets Interface facing to the server for default host.
*
* @param serverInfo server information
* @return the Interface facing to the server; null if not found
*/
private Interface getServerInterface(DhcpServerInfo serverInfo) {
Interface serverInterface = null;
ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
.stream()
.filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
.findFirst()
.orElse(null);
} else {
log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
dhcpServerConnectPoint, dhcpConnectVlan);
}
return serverInterface;
}
//forward the packet to ConnectPoint where the DHCP server is attached.
private void forwardPacket(InternalPacket packet) {
//send Packetout to dhcp server connectpoint.
if (packet.destLocation != null) {
TrafficTreatment t = DefaultTrafficTreatment.builder()
.setOutput(packet.destLocation.port()).build();
OutboundPacket o = new DefaultOutboundPacket(
packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
if (log.isTraceEnabled()) {
log.trace("Relaying packet to destination {}", packet.destLocation);
}
log.info("DHCP RELAY: packetService.emit(o) to port {}", packet.destLocation);
packetService.emit(o);
}
}
private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
DhcpServerInfo foundServerInfo = null;
for (DhcpServerInfo serverInfo : validServerInfoList) {
if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
foundServerInfo = serverInfo;
log.warn("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
break;
} else {
log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
}
}
return foundServerInfo;
}
}

View File

@ -0,0 +1,104 @@
/*
* 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.onlab.packet.Ip4Address;
import org.onlab.packet.VlanId;
import org.onlab.packet.Ethernet;
import org.onlab.util.HexString;
import org.onosproject.dhcprelay.api.DhcpServerInfo;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.intf.Interface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
public class Dhcp4HandlerUtil {
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* Returns the first v4 interface ip out of a set of interfaces or null.
*
* @param intfs set of interfaces
* @return Ip4Address / null if not present
*/
public 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;
}
/**
* Determind if an Interface contains a vlan id.
*
* @param iface the Interface
* @param vlanId the vlan id
* @return true if the Interface contains the vlan id
*/
public boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
if (vlanId.equals(VlanId.NONE)) {
// untagged packet, check if vlan untagged or vlan native is not NONE
return !iface.vlanUntagged().equals(VlanId.NONE) ||
!iface.vlanNative().equals(VlanId.NONE);
}
// tagged packet, check if the interface contains the vlan
return iface.vlanTagged().contains(vlanId);
}
/**
* Check if a given server info has v6 ipaddress.
*
* @param serverInfo server info to check
* @return true if server info has v6 ip address; false otherwise
*/
public boolean isServerIpEmpty(DhcpServerInfo serverInfo) {
if (!serverInfo.getDhcpServerIp4().isPresent()) {
log.warn("DhcpServerIp not available, use default DhcpServerIp {}",
HexString.toHexString(serverInfo.getDhcpServerIp4().get().toOctets()));
return true;
}
return false;
}
/**
* the new class the contains Ethernet packet and destination port.
*/
public class InternalPacket {
Ethernet packet;
ConnectPoint destLocation;
public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
packet = newPacket;
destLocation = newLocation;
}
}
}