blob: 26643fc017fc47fec92e24396c275a5baa62192d [file] [log] [blame]
Yi Tseng51301292017-07-28 13:02:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.onosproject.dhcprelay;
19
Yi Tseng525ff402017-10-23 19:39:39 -070020import com.google.common.collect.HashMultimap;
21import com.google.common.collect.ImmutableSet;
Charles Chane5e0c9a2018-03-30 12:11:34 -070022import com.google.common.collect.Lists;
Yi Tseng525ff402017-10-23 19:39:39 -070023import com.google.common.collect.Multimap;
Charles Chan70fdd492018-03-07 17:36:06 -080024import com.google.common.collect.Multimaps;
Yi Tseng51301292017-07-28 13:02:59 -070025import com.google.common.collect.Sets;
Yi Tseng483ac6f2017-08-02 15:03:31 -070026import org.apache.felix.scr.annotations.Activate;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
Yi Tseng483ac6f2017-08-02 15:03:31 -070028import org.apache.felix.scr.annotations.Deactivate;
Yi Tseng51301292017-07-28 13:02:59 -070029import org.apache.felix.scr.annotations.Property;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
32import org.apache.felix.scr.annotations.Service;
33import org.onlab.packet.BasePacket;
34import org.onlab.packet.DHCP;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.IPv4;
37import org.onlab.packet.Ip4Address;
38import org.onlab.packet.IpAddress;
39import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Yi Tseng51301292017-07-28 13:02:59 -070041import org.onlab.packet.UDP;
42import org.onlab.packet.VlanId;
43import org.onlab.packet.dhcp.CircuitId;
44import org.onlab.packet.dhcp.DhcpOption;
45import org.onlab.packet.dhcp.DhcpRelayAgentOption;
Yi Tseng525ff402017-10-23 19:39:39 -070046import org.onosproject.core.ApplicationId;
47import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070048import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070049import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng483ac6f2017-08-02 15:03:31 -070050import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng525ff402017-10-23 19:39:39 -070051import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Yi Tseng51301292017-07-28 13:02:59 -070052import org.onosproject.dhcprelay.store.DhcpRecord;
53import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng525ff402017-10-23 19:39:39 -070054import org.onosproject.net.Device;
55import org.onosproject.net.DeviceId;
56import org.onosproject.net.behaviour.Pipeliner;
57import org.onosproject.net.device.DeviceService;
58import org.onosproject.net.flow.DefaultTrafficSelector;
59import org.onosproject.net.flow.TrafficSelector;
60import org.onosproject.net.flowobjective.DefaultForwardingObjective;
61import org.onosproject.net.flowobjective.FlowObjectiveService;
62import org.onosproject.net.flowobjective.ForwardingObjective;
63import org.onosproject.net.flowobjective.Objective;
64import org.onosproject.net.flowobjective.ObjectiveContext;
65import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng483ac6f2017-08-02 15:03:31 -070066import org.onosproject.net.host.HostEvent;
67import org.onosproject.net.host.HostListener;
Yi Tsengaa417a62017-09-08 17:22:51 -070068import org.onosproject.net.host.HostProvider;
69import org.onosproject.net.host.HostProviderRegistry;
70import org.onosproject.net.host.HostProviderService;
Ray Milkeyfacf2862017-08-03 11:58:29 -070071import org.onosproject.net.intf.Interface;
72import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070073import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070074import org.onosproject.net.provider.ProviderId;
Ray Milkey69ec8712017-08-08 13:00:43 -070075import org.onosproject.routeservice.Route;
76import org.onosproject.routeservice.RouteStore;
Yi Tseng51301292017-07-28 13:02:59 -070077import org.onosproject.net.ConnectPoint;
78import org.onosproject.net.Host;
79import org.onosproject.net.HostId;
80import org.onosproject.net.HostLocation;
81import org.onosproject.net.flow.DefaultTrafficTreatment;
82import org.onosproject.net.flow.TrafficTreatment;
83import org.onosproject.net.host.DefaultHostDescription;
84import org.onosproject.net.host.HostDescription;
85import org.onosproject.net.host.HostService;
Yi Tseng51301292017-07-28 13:02:59 -070086import org.onosproject.net.host.InterfaceIpAddress;
87import org.onosproject.net.packet.DefaultOutboundPacket;
88import org.onosproject.net.packet.OutboundPacket;
89import org.onosproject.net.packet.PacketContext;
90import org.onosproject.net.packet.PacketService;
91import org.slf4j.Logger;
92import org.slf4j.LoggerFactory;
93
94import java.nio.ByteBuffer;
Yi Tsengdcef2c22017-08-05 20:34:06 -070095import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -070096import java.util.Collections;
97import java.util.List;
rsahot036620655b2018-02-26 15:01:37 -050098import java.util.ArrayList;
Yi Tseng51301292017-07-28 13:02:59 -070099import java.util.Optional;
100import java.util.Set;
Charles Chan909cff82018-03-05 13:14:02 -0800101import java.util.concurrent.CopyOnWriteArrayList;
Jordan Halterman6328db72018-04-10 13:34:50 -0400102import java.util.concurrent.Executor;
Yi Tseng525ff402017-10-23 19:39:39 -0700103import java.util.concurrent.atomic.AtomicInteger;
Yi Tseng51301292017-07-28 13:02:59 -0700104import java.util.stream.Collectors;
105
106import static com.google.common.base.Preconditions.checkNotNull;
107import static com.google.common.base.Preconditions.checkState;
Jordan Halterman6328db72018-04-10 13:34:50 -0400108import static java.util.concurrent.Executors.newSingleThreadExecutor;
Yi Tseng51301292017-07-28 13:02:59 -0700109import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
110import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
111import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
112import static org.onlab.packet.MacAddress.valueOf;
113import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID;
Jordan Halterman6328db72018-04-10 13:34:50 -0400114import static org.onlab.util.Tools.groupedThreads;
Yi Tseng525ff402017-10-23 19:39:39 -0700115import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
116import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700117
rsahot036620655b2018-02-26 15:01:37 -0500118import org.onosproject.dhcprelay.Dhcp4HandlerUtil.InternalPacket;
119
Yi Tseng51301292017-07-28 13:02:59 -0700120@Component
121@Service
122@Property(name = "version", value = "4")
Yi Tsengaa417a62017-09-08 17:22:51 -0700123public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700124 public static final String DHCP_V4_RELAY_APP = "org.onosproject.Dhcp4HandlerImpl";
125 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp4", DHCP_V4_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700126 private static final String BROADCAST_IP = "255.255.255.255";
127 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
128
129 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
130 .matchEthType(Ethernet.TYPE_IPV4)
131 .matchIPProtocol(IPv4.PROTOCOL_UDP)
132 .matchIPSrc(Ip4Address.ZERO.toIpPrefix())
133 .matchIPDst(Ip4Address.valueOf(BROADCAST_IP).toIpPrefix())
134 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
135 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
136 .build();
137 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
138 .matchEthType(Ethernet.TYPE_IPV4)
139 .matchIPProtocol(IPv4.PROTOCOL_UDP)
140 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
141 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
142 .build();
143 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
144 CLIENT_SERVER_SELECTOR,
145 SERVER_RELAY_SELECTOR
146 );
Yi Tseng51301292017-07-28 13:02:59 -0700147 private static Logger log = LoggerFactory.getLogger(Dhcp4HandlerImpl.class);
148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
150 protected DhcpRelayStore dhcpRelayStore;
151
152 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
153 protected PacketService packetService;
154
155 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng51301292017-07-28 13:02:59 -0700156 protected RouteStore routeStore;
157
158 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
159 protected InterfaceService interfaceService;
160
161 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
162 protected HostService hostService;
163
Yi Tsengaa417a62017-09-08 17:22:51 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
165 protected HostProviderRegistry providerRegistry;
166
Yi Tseng525ff402017-10-23 19:39:39 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
168 protected CoreService coreService;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
171 protected DeviceService deviceService;
172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
174 protected FlowObjectiveService flowObjectiveService;
175
Yi Tsengaa417a62017-09-08 17:22:51 -0700176 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700177 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800178 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng483ac6f2017-08-02 15:03:31 -0700179 private InternalHostListener hostListener = new InternalHostListener();
180
Charles Chan909cff82018-03-05 13:14:02 -0800181 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
182 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
rsahot036620655b2018-02-26 15:01:37 -0500183 private Dhcp4HandlerUtil dhcp4HandlerUtil = new Dhcp4HandlerUtil();
Yi Tseng4f2a0462017-08-31 11:21:00 -0700184
Jordan Halterman6328db72018-04-10 13:34:50 -0400185 private Executor hostEventExecutor = newSingleThreadExecutor(
186 groupedThreads("dhcp4-event-host", "%d", log));
187
Yi Tseng483ac6f2017-08-02 15:03:31 -0700188 @Activate
189 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700190 appId = coreService.registerApplication(DHCP_V4_RELAY_APP);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700191 hostService.addListener(hostListener);
Yi Tsengaa417a62017-09-08 17:22:51 -0700192 providerService = providerRegistry.register(this);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700193 }
194
195 @Deactivate
196 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700197 providerRegistry.unregister(this);
198 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700199 defaultServerInfoList.forEach(this::stopMonitoringIps);
200 defaultServerInfoList.clear();
201 indirectServerInfoList.forEach(this::stopMonitoringIps);
202 indirectServerInfoList.clear();
Yi Tseng483ac6f2017-08-02 15:03:31 -0700203 }
204
Yi Tseng919b2df2017-09-07 16:22:51 -0700205 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
206 serverInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
207 hostService.stopMonitoringIp(gatewayIp);
208 });
209 serverInfo.getDhcpServerIp4().ifPresent(serverIp -> {
210 hostService.stopMonitoringIp(serverIp);
211 });
Yi Tseng51301292017-07-28 13:02:59 -0700212 }
213
214 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700215 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -0700216 setDhcpServerConfigs(configs, defaultServerInfoList);
217 }
218
219 @Override
220 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
221 setDhcpServerConfigs(configs, indirectServerInfoList);
222 }
223
224 @Override
225 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
226 return defaultServerInfoList;
227 }
228
229 @Override
230 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
231 return indirectServerInfoList;
232 }
233
Yi Tseng525ff402017-10-23 19:39:39 -0700234 @Override
235 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
236 if (config == null) {
237 ignoredVlans.forEach(((deviceId, vlanId) -> {
238 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
239 }));
240 return;
241 }
242 config.ignoredVlans().forEach((deviceId, vlanId) -> {
243 if (ignoredVlans.get(deviceId).contains(vlanId)) {
244 // don't need to process if it already ignored
245 return;
246 }
247 processIgnoreVlanRule(deviceId, vlanId, ADD);
248 });
249
250 ignoredVlans.forEach((deviceId, vlanId) -> {
251 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
252 // not contains in new config, remove it
253 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
254 }
255 });
256 }
257
Saurav Dasb805f1a2017-12-13 16:19:35 -0800258 @Override
259 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
260 if (config == null) {
261 ignoredVlans.clear();
262 return;
263 }
264 config.ignoredVlans().forEach((deviceId, vlanId) -> {
265 ignoredVlans.remove(deviceId, vlanId);
266 });
267 }
268
Yi Tseng919b2df2017-09-07 16:22:51 -0700269 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700270 if (configs.size() == 0) {
271 // no config to update
272 return;
273 }
274
rsahot036620655b2018-02-26 15:01:37 -0500275 Boolean isConfigValid = false;
276 for (DhcpServerConfig serverConfig : configs) {
277 if (serverConfig.getDhcpServerIp4().isPresent()) {
278 isConfigValid = true;
279 break;
280 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700281 }
rsahot036620655b2018-02-26 15:01:37 -0500282 if (!isConfigValid) {
283 log.warn("No IP V4 server address found.");
284 return; // No IP V6 address found
285 }
286 // if (!serverInfoList.isEmpty()) {
287 for (DhcpServerInfo oldServerInfo : serverInfoList) {
288 log.info("In for (DhcpServerInfo oldServerInfo : serverInfoList) {");
Yi Tseng919b2df2017-09-07 16:22:51 -0700289 // remove old server info
rsahot036620655b2018-02-26 15:01:37 -0500290 //DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700291
Yi Tseng919b2df2017-09-07 16:22:51 -0700292 // stop monitoring gateway or server
293 oldServerInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
294 hostService.stopMonitoringIp(gatewayIp);
295 });
296 oldServerInfo.getDhcpServerIp4().ifPresent(serverIp -> {
297 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -0700298 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -0700299 });
Yi Tseng483ac6f2017-08-02 15:03:31 -0700300 }
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700301
Yi Tseng919b2df2017-09-07 16:22:51 -0700302 // Create new server info according to the config
rsahot036620655b2018-02-26 15:01:37 -0500303 serverInfoList.clear();
304 for (DhcpServerConfig serverConfig : configs) {
305 log.info("// Create new server info according to the config");
306 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
307 DhcpServerInfo.Version.DHCP_V4);
308 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
309 "Connect point not exists");
310 checkState(newServerInfo.getDhcpServerIp4().isPresent(),
311 "IP of DHCP server not exists");
Yi Tseng4f2a0462017-08-31 11:21:00 -0700312
rsahot036620655b2018-02-26 15:01:37 -0500313 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
314 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -0700315
rsahot036620655b2018-02-26 15:01:37 -0500316 Ip4Address serverIp = newServerInfo.getDhcpServerIp4().get();
317 Ip4Address ipToProbe;
318 if (newServerInfo.getDhcpGatewayIp4().isPresent()) {
319 ipToProbe = newServerInfo.getDhcpGatewayIp4().get();
320 } else {
321 ipToProbe = newServerInfo.getDhcpServerIp4().orElse(null);
322 }
323 log.info("Probe_IP {}", ipToProbe);
324 String hostToProbe = newServerInfo.getDhcpGatewayIp4()
325 .map(ip -> "gateway").orElse("server");
326
327 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
328 hostService.startMonitoringIp(ipToProbe);
329
330 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
331 if (!hosts.isEmpty()) {
332 Host host = hosts.iterator().next();
333 newServerInfo.setDhcpConnectVlan(host.vlan());
334 newServerInfo.setDhcpConnectMac(host.mac());
335 }
336
337 // Add new server info
338 synchronized (this) {
339 //serverInfoList.clear();
340 serverInfoList.add(newServerInfo);
341 }
342
343 requestDhcpPacket(serverIp);
Yi Tseng4f2a0462017-08-31 11:21:00 -0700344 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700345 }
346
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700347 @Override
Yi Tseng51301292017-07-28 13:02:59 -0700348 public void processDhcpPacket(PacketContext context, BasePacket payload) {
349 checkNotNull(payload, "DHCP payload can't be null");
350 checkState(payload instanceof DHCP, "Payload is not a DHCP");
351 DHCP dhcpPayload = (DHCP) payload;
352 if (!configured()) {
Yi Tseng919b2df2017-09-07 16:22:51 -0700353 log.warn("Missing default DHCP relay server config. Abort packet processing");
Yi Tseng51301292017-07-28 13:02:59 -0700354 return;
355 }
356
357 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tseng51301292017-07-28 13:02:59 -0700358 checkNotNull(dhcpPayload, "Can't find DHCP payload");
359 Ethernet packet = context.inPacket().parsed();
360 DHCP.MsgType incomingPacketType = dhcpPayload.getOptions().stream()
361 .filter(dhcpOption -> dhcpOption.getCode() == OptionCode_MessageType.getValue())
362 .map(DhcpOption::getData)
363 .map(data -> DHCP.MsgType.getType(data[0]))
364 .findFirst()
365 .orElse(null);
366 checkNotNull(incomingPacketType, "Can't get message type from DHCP payload {}", dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500367 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
368 //ignore the packets if dhcp client interface is not configured on onos.
369 if (receivingInterfaces.isEmpty()) {
370 log.warn("Virtual interface is not configured on {}", inPort);
371 return;
372 }
Yi Tseng51301292017-07-28 13:02:59 -0700373 switch (incomingPacketType) {
374 case DHCPDISCOVER:
Yi Tsengdcef2c22017-08-05 20:34:06 -0700375 // Add the gateway IP as virtual interface IP for server to understand
Yi Tseng51301292017-07-28 13:02:59 -0700376 // the lease to be assigned and forward the packet to dhcp server.
rsahot036620655b2018-02-26 15:01:37 -0500377 List<InternalPacket> ethernetClientPacket =
378 processDhcpPacketFromClient(context, packet, receivingInterfaces);
379 for (InternalPacket internalPacket : ethernetClientPacket) {
380 log.debug("DHCPDISCOVER from {} Forward to server", inPort);
Yi Tseng51301292017-07-28 13:02:59 -0700381 writeRequestDhcpRecord(inPort, packet, dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500382 forwardPacket(internalPacket);
Yi Tseng51301292017-07-28 13:02:59 -0700383 }
384 break;
385 case DHCPOFFER:
386 //reply to dhcp client.
rsahot036620655b2018-02-26 15:01:37 -0500387 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(context, packet);
Yi Tseng51301292017-07-28 13:02:59 -0700388 if (ethernetPacketOffer != null) {
389 writeResponseDhcpRecord(ethernetPacketOffer, dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700390 sendResponseToClient(ethernetPacketOffer, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -0700391 }
392 break;
393 case DHCPREQUEST:
394 // add the gateway ip as virtual interface ip for server to understand
395 // the lease to be assigned and forward the packet to dhcp server.
rsahot036620655b2018-02-26 15:01:37 -0500396 List<InternalPacket> ethernetPacketRequest =
397 processDhcpPacketFromClient(context, packet, receivingInterfaces);
398 for (InternalPacket internalPacket : ethernetPacketRequest) {
399 log.debug("DHCPDISCOVER from {} Forward to server", inPort);
Yi Tseng51301292017-07-28 13:02:59 -0700400 writeRequestDhcpRecord(inPort, packet, dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500401 forwardPacket(internalPacket);
Yi Tseng51301292017-07-28 13:02:59 -0700402 }
403 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400404 case DHCPDECLINE:
405 break;
Yi Tseng51301292017-07-28 13:02:59 -0700406 case DHCPACK:
407 // reply to dhcp client.
rsahot036620655b2018-02-26 15:01:37 -0500408 Ethernet ethernetPacketAck = processDhcpPacketFromServer(context, packet);
Yi Tseng51301292017-07-28 13:02:59 -0700409 if (ethernetPacketAck != null) {
410 writeResponseDhcpRecord(ethernetPacketAck, dhcpPayload);
411 handleDhcpAck(ethernetPacketAck, dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700412 sendResponseToClient(ethernetPacketAck, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -0700413 }
414 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400415 case DHCPNAK:
416 break;
Yi Tseng51301292017-07-28 13:02:59 -0700417 case DHCPRELEASE:
418 // TODO: release the ip address from client
419 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400420 case DHCPINFORM:
421 break;
422 case DHCPFORCERENEW:
423 break;
424 case DHCPLEASEQUERY:
425 handleLeaseQueryMsg(context, packet, dhcpPayload);
426 break;
427 case DHCPLEASEACTIVE:
428 handleLeaseQueryActivateMsg(packet, dhcpPayload);
429 break;
430 case DHCPLEASEUNASSIGNED:
431 case DHCPLEASEUNKNOWN:
432 handleLeaseQueryUnknown(packet, dhcpPayload);
433 break;
Yi Tseng51301292017-07-28 13:02:59 -0700434 default:
435 break;
436 }
437 }
438
439 /**
440 * Checks if this app has been configured.
441 *
442 * @return true if all information we need have been initialized
443 */
Yi Tseng4f2a0462017-08-31 11:21:00 -0700444 private boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700445 return !defaultServerInfoList.isEmpty();
Yi Tseng51301292017-07-28 13:02:59 -0700446 }
447
448 /**
Yi Tsengdcef2c22017-08-05 20:34:06 -0700449 * Returns the first interface ip from interface.
Yi Tseng51301292017-07-28 13:02:59 -0700450 *
Yi Tsengdcef2c22017-08-05 20:34:06 -0700451 * @param iface interface of one connect point
Yi Tseng51301292017-07-28 13:02:59 -0700452 * @return the first interface IP; null if not exists an IP address in
453 * these interfaces
454 */
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700455 private Ip4Address getFirstIpFromInterface(Interface iface) {
Yi Tsengdcef2c22017-08-05 20:34:06 -0700456 checkNotNull(iface, "Interface can't be null");
457 return iface.ipAddressesList().stream()
Yi Tseng51301292017-07-28 13:02:59 -0700458 .map(InterfaceIpAddress::ipAddress)
459 .filter(IpAddress::isIp4)
460 .map(IpAddress::getIp4Address)
461 .findFirst()
462 .orElse(null);
463 }
464
465 /**
Yi Tseng4f2a0462017-08-31 11:21:00 -0700466 * Gets Interface facing to the server for default host.
Yi Tsengdcef2c22017-08-05 20:34:06 -0700467 *
468 * @return the Interface facing to the server; null if not found
469 */
Yi Tseng919b2df2017-09-07 16:22:51 -0700470 private Interface getDefaultServerInterface() {
471 return getServerInterface(defaultServerInfoList);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700472 }
473
474 /**
Yi Tseng4f2a0462017-08-31 11:21:00 -0700475 * Gets Interface facing to the server for indirect hosts.
476 * Use default server Interface if indirect server not configured.
477 *
478 * @return the Interface facing to the server; null if not found
479 */
480 private Interface getIndirectServerInterface() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700481 return getServerInterface(indirectServerInfoList);
482 }
483
484 private Interface getServerInterface(List<DhcpServerInfo> serverInfos) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800485 return serverInfos.stream()
Yi Tseng4f2a0462017-08-31 11:21:00 -0700486 .findFirst()
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800487 .map(serverInfo -> {
488 ConnectPoint dhcpServerConnectPoint =
489 serverInfo.getDhcpServerConnectPoint().orElse(null);
490 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
491 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
492 return null;
493 }
494 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
495 .stream()
496 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
497 .findFirst()
498 .orElse(null);
499 })
Yi Tseng4f2a0462017-08-31 11:21:00 -0700500 .orElse(null);
501 }
502
503 /**
504 * Determind if an Interface contains a vlan id.
505 *
506 * @param iface the Interface
507 * @param vlanId the vlan id
508 * @return true if the Interface contains the vlan id
509 */
510 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
Yi Tseng4025a102017-09-30 11:35:42 +0800511 if (vlanId.equals(VlanId.NONE)) {
512 // untagged packet, check if vlan untagged or vlan native is not NONE
513 return !iface.vlanUntagged().equals(VlanId.NONE) ||
514 !iface.vlanNative().equals(VlanId.NONE);
515 }
516 // tagged packet, check if the interface contains the vlan
517 return iface.vlanTagged().contains(vlanId);
Yi Tseng4f2a0462017-08-31 11:21:00 -0700518 }
519
Charles Chand0dd7002017-10-08 23:53:36 -0400520 private void handleLeaseQueryActivateMsg(Ethernet packet, DHCP dhcpPayload) {
521 log.debug("LQ: Got DHCPLEASEACTIVE packet!");
522
523 // TODO: release the ip address from client
524 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
525 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
526 HostId hostId = HostId.hostId(clientMacAddress, vlanId);
527 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
528
529 if (record == null) {
530 log.warn("Can't find record for host {} when processing DHCPLEASEACTIVE", hostId);
531 return;
532 }
533
534 // need to update routes
535 log.debug("Lease Query for Client results in DHCPLEASEACTIVE - route needs to be modified");
536 // get current route
537 // find the ip of that client with the DhcpRelay store
538
539 Ip4Address clientIP = record.ip4Address().orElse(null);
540 log.debug("LQ: IP of host is " + clientIP.getIp4Address());
541
542 MacAddress nextHopMac = record.nextHop().orElse(null);
543 log.debug("LQ: MAC of resulting *OLD* NH for that host is " + nextHopMac.toString());
544
545 // find the new NH by looking at the src MAC of the dhcp request
546 // from the LQ store
547 MacAddress newNextHopMac = record.nextHopTemp().orElse(null);
548 log.debug("LQ: MAC of resulting *NEW* NH for that host is " + newNextHopMac.toString());
549
550 log.debug("LQ: updating dhcp relay record with new NH");
551 record.nextHop(newNextHopMac);
552
553 // find the next hop IP from its mac
554 HostId gwHostId = HostId.hostId(newNextHopMac, vlanId);
555 Host gwHost = hostService.getHost(gwHostId);
556
557 if (gwHost == null) {
558 log.warn("Can't find gateway for new NH host " + gwHostId);
559 return;
560 }
561
562 Ip4Address nextHopIp = gwHost.ipAddresses()
563 .stream()
564 .filter(IpAddress::isIp4)
565 .map(IpAddress::getIp4Address)
566 .findFirst()
567 .orElse(null);
568
569 if (nextHopIp == null) {
570 log.warn("Can't find IP address of gateway " + gwHost);
571 return;
572 }
573
574 log.debug("LQ: *NEW* NH IP for host is " + nextHopIp.getIp4Address());
Charles Chan6305b692018-04-04 11:43:54 -0700575 Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
Charles Chand0dd7002017-10-08 23:53:36 -0400576 routeStore.updateRoute(route);
577
578 // and forward to client
579 Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
580 if (ethernetPacket != null) {
581 sendResponseToClient(ethernetPacket, dhcpPayload);
582 }
583 }
584
585 /**
586 *
587 */
588 private void handleLeaseQueryMsg(PacketContext context, Ethernet packet, DHCP dhcpPayload) {
589 log.debug("LQ: Got DHCPLEASEQUERY packet!");
590 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
591 log.debug("LQ: got DHCPLEASEQUERY with MAC " + clientMacAddress.toString());
592 // add the client mac (hostid) of this request to a store (the entry will be removed with
593 // the reply sent to the originator)
594 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
595 HostId hId = HostId.hostId(clientMacAddress, vlanId);
596 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hId).orElse(null);
597 if (record != null) {
598 //new NH is to be taken from src mac of LQ packet
599 MacAddress newNextHop = packet.getSourceMAC();
600 record.nextHopTemp(newNextHop);
601 record.ip4Status(dhcpPayload.getPacketType());
602 record.updateLastSeen();
603
604 // do a basic routing of the packet (this is unicast routing
605 // not a relay operation like for other broadcast dhcp packets
rsahot036620655b2018-02-26 15:01:37 -0500606 List<InternalPacket> ethernetPacketRequest = processLeaseQueryFromAgent(context, packet);
Charles Chand0dd7002017-10-08 23:53:36 -0400607 // and forward to server
rsahot036620655b2018-02-26 15:01:37 -0500608 for (InternalPacket internalPacket : ethernetPacketRequest) {
609 log.debug("LeaseQueryMsg forward to server");
610 forwardPacket(internalPacket);
611 }
Charles Chand0dd7002017-10-08 23:53:36 -0400612 } else {
613 log.warn("LQ: Error! - DHCP relay record for that client not found - ignoring LQ!");
614 }
615 }
616
617 private void handleLeaseQueryUnknown(Ethernet packet, DHCP dhcpPayload) {
618 log.debug("Lease Query for Client results in DHCPLEASEUNASSIGNED or " +
619 "DHCPLEASEUNKNOWN - removing route & forwarding reply to originator");
620 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
621 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
622 HostId hostId = HostId.hostId(clientMacAddress, vlanId);
623 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
624
625 if (record == null) {
626 log.warn("Can't find record for host {} when handling LQ UNKNOWN/UNASSIGNED message", hostId);
627 return;
628 }
629
630 Ip4Address clientIP = record.ip4Address().orElse(null);
631 log.debug("LQ: IP of host is " + clientIP.getIp4Address());
632
633 // find the new NH by looking at the src MAC of the dhcp request
634 // from the LQ store
635 MacAddress nextHopMac = record.nextHop().orElse(null);
636 log.debug("LQ: MAC of resulting *Existing* NH for that route is " + nextHopMac.toString());
637
638 // find the next hop IP from its mac
639 HostId gwHostId = HostId.hostId(nextHopMac, vlanId);
640 Host gwHost = hostService.getHost(gwHostId);
641
642 if (gwHost == null) {
643 log.warn("Can't find gateway for new NH host " + gwHostId);
644 return;
645 }
646
647 Ip4Address nextHopIp = gwHost.ipAddresses()
648 .stream()
649 .filter(IpAddress::isIp4)
650 .map(IpAddress::getIp4Address)
651 .findFirst()
652 .orElse(null);
653
654 if (nextHopIp == null) {
655 log.warn("Can't find IP address of gateway {}", gwHost);
656 return;
657 }
658
659 log.debug("LQ: *Existing* NH IP for host is " + nextHopIp.getIp4Address() + " removing route for it");
Charles Chan6305b692018-04-04 11:43:54 -0700660 Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
Charles Chand0dd7002017-10-08 23:53:36 -0400661 routeStore.removeRoute(route);
662
663 // remove from temp store
664 dhcpRelayStore.removeDhcpRecord(hostId);
665
666 // and forward to client
667 Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
668 if (ethernetPacket != null) {
669 sendResponseToClient(ethernetPacket, dhcpPayload);
670 }
671 }
672
Yi Tseng4f2a0462017-08-31 11:21:00 -0700673 /**
Yi Tseng51301292017-07-28 13:02:59 -0700674 * Build the DHCP discover/request packet with gateway IP(unicast packet).
675 *
676 * @param context the packet context
677 * @param ethernetPacket the ethernet payload to process
Yi Tseng51301292017-07-28 13:02:59 -0700678 * @return processed packet
679 */
rsahot036620655b2018-02-26 15:01:37 -0500680 private List<InternalPacket> processDhcpPacketFromClient(PacketContext context,
681 Ethernet ethernetPacket,
682 Set<Interface> clientInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -0700683 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
684 DeviceId receivedFromDevice = receivedFrom.deviceId();
rsahot036620655b2018-02-26 15:01:37 -0500685 Ip4Address relayAgentIp = null;
686 relayAgentIp = dhcp4HandlerUtil.getRelayAgentIPv4Address(clientInterfaces);
687 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
688 if (relayAgentIp == null || relayAgentMac == null) {
689 log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
690 + "packet from client on port: {}. Aborting packet processing",
691 clientInterfaces.iterator().next().connectPoint());
Charles Chane5e0c9a2018-03-30 12:11:34 -0700692 return Lists.newArrayList();
rsahot036620655b2018-02-26 15:01:37 -0500693 }
694 log.debug("Multi DHCP V4 processDhcpPacketFromClient on port {}",
695 clientInterfaces.iterator().next().connectPoint());
Yi Tseng25bfe372017-11-03 16:27:32 -0700696
Yi Tseng4f2a0462017-08-31 11:21:00 -0700697 // get dhcp header.
rsahot036620655b2018-02-26 15:01:37 -0500698 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Yi Tseng4f2a0462017-08-31 11:21:00 -0700699 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
700 UDP udpPacket = (UDP) ipv4Packet.getPayload();
701 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
702
Yi Tseng919b2df2017-09-07 16:22:51 -0700703
Yi Tsengdcef2c22017-08-05 20:34:06 -0700704 Ip4Address clientInterfaceIp =
705 interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
706 .stream()
707 .map(Interface::ipAddressesList)
708 .flatMap(Collection::stream)
709 .map(InterfaceIpAddress::ipAddress)
710 .filter(IpAddress::isIp4)
711 .map(IpAddress::getIp4Address)
712 .findFirst()
713 .orElse(null);
714 if (clientInterfaceIp == null) {
715 log.warn("Can't find interface IP for client interface for port {}",
rsahot036620655b2018-02-26 15:01:37 -0500716 context.inPacket().receivedFrom());
Charles Chane5e0c9a2018-03-30 12:11:34 -0700717 return Lists.newArrayList();
Yi Tsengdcef2c22017-08-05 20:34:06 -0700718 }
rsahot036620655b2018-02-26 15:01:37 -0500719
Yi Tseng4f2a0462017-08-31 11:21:00 -0700720 boolean isDirectlyConnected = directlyConnected(dhcpPacket);
rsahot036620655b2018-02-26 15:01:37 -0500721 boolean directConnFlag = directlyConnected(dhcpPacket);
722
723 // Multi DHCP Start
724 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
725 VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
726 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
727 .stream().filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
728 .findFirst()
729 .orElse(null);
730
731 List<InternalPacket> internalPackets = new ArrayList<>();
732 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
733 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
734
735
736 for (DhcpServerInfo serverInfo : copyServerInfoList) {
737 etherReply = (Ethernet) ethernetPacket.clone();
738 ipv4Packet = (IPv4) etherReply.getPayload();
739 udpPacket = (UDP) ipv4Packet.getPayload();
740 dhcpPacket = (DHCP) udpPacket.getPayload();
741 if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
742 log.warn("Can't get server connect point, ignore");
743 continue;
744 }
745 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
746 if (newServerInfo == null) {
747 log.warn("Can't get server interface with host info resolved, ignore");
748 continue;
749 }
750
751 Interface serverInterface = getServerInterface(newServerInfo);
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800752 if (serverInterface == null) {
rsahot036620655b2018-02-26 15:01:37 -0500753 log.warn("Can't get server interface, ignore");
754 continue;
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800755 }
Yi Tseng4f2a0462017-08-31 11:21:00 -0700756
rsahot036620655b2018-02-26 15:01:37 -0500757 Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
758 MacAddress macFacingServer = serverInterface.mac();
759 log.debug("Interfacing server {} Mac : {} ", ipFacingServer, macFacingServer);
760 if (ipFacingServer == null || macFacingServer == null) {
761 log.warn("No IP address for server Interface {}", serverInterface);
rsahot036620655b2018-02-26 15:01:37 -0500762 continue;
763 }
Yi Tseng51301292017-07-28 13:02:59 -0700764
Charles Chand0d1e332017-10-10 16:53:32 -0400765
rsahot036620655b2018-02-26 15:01:37 -0500766 etherReply.setSourceMACAddress(macFacingServer);
767 // set default info and replace with indirect if available later on.
768 if (newServerInfo.getDhcpConnectMac().isPresent()) {
769 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
770 }
771 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
772 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
773 }
774 ipv4Packet.setSourceAddress(ipFacingServer.toInt());
775 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
Charles Chan2de55302018-03-02 18:27:59 -0800776 log.debug("Directly connected {}", isDirectlyConnected);
777 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().get());
rsahot036620655b2018-02-26 15:01:37 -0500778 if (isDirectlyConnected) {
Yi Tseng51301292017-07-28 13:02:59 -0700779
Charles Chan2de55302018-03-02 18:27:59 -0800780 log.debug("Default DHCP server IP: {}", newServerInfo.getDhcpServerIp4().get());
rsahot036620655b2018-02-26 15:01:37 -0500781 if (newServerInfo.getDhcpConnectMac().isPresent()) {
782 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
Charles Chand0d1e332017-10-10 16:53:32 -0400783 }
rsahot036620655b2018-02-26 15:01:37 -0500784 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
785 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
786 }
787
788 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
789
790
791 ConnectPoint inPort = context.inPacket().receivedFrom();
792 VlanId vlanId = VlanId.vlanId(ethernetPacket.getVlanID());
793 // add connected in port and vlan
794 CircuitId cid = new CircuitId(inPort.toString(), vlanId);
795 byte[] circuitId = cid.serialize();
796 DhcpOption circuitIdSubOpt = new DhcpOption();
797 circuitIdSubOpt
798 .setCode(CIRCUIT_ID.getValue())
799 .setLength((byte) circuitId.length)
800 .setData(circuitId);
801
802 DhcpRelayAgentOption newRelayAgentOpt = new DhcpRelayAgentOption();
803 newRelayAgentOpt.setCode(OptionCode_CircuitID.getValue());
804 newRelayAgentOpt.addSubOption(circuitIdSubOpt);
805
806 // Removes END option first
807 List<DhcpOption> options = dhcpPacket.getOptions().stream()
808 .filter(opt -> opt.getCode() != OptionCode_END.getValue())
809 .collect(Collectors.toList());
810
811 // push relay agent option
812 options.add(newRelayAgentOpt);
813
814 // make sure option 255(End) is the last option
815 DhcpOption endOption = new DhcpOption();
816 endOption.setCode(OptionCode_END.getValue());
817 options.add(endOption);
818
819 dhcpPacket.setOptions(options);
820
821 relayAgentIp = serverInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
822
823 // Sets relay agent IP
824 int effectiveRelayAgentIp = relayAgentIp != null ?
825 relayAgentIp.toInt() : clientInterfaceIp.toInt();
826 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
Charles Chan2de55302018-03-02 18:27:59 -0800827 log.debug("In Default, Relay Agent IP {}", effectiveRelayAgentIp);
Charles Chand0d1e332017-10-10 16:53:32 -0400828 } else {
rsahot036620655b2018-02-26 15:01:37 -0500829 if (!newServerInfo.getDhcpServerIp4().isPresent()) {
830 // do nothing
831 } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
832 continue;
833 } else {
834 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
835 // Sets relay agent IP
836 int effectiveRelayAgentIp = relayAgentIp != null ?
837 relayAgentIp.toInt() : clientInterfaceIp.toInt();
838 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
Charles Chand0d1e332017-10-10 16:53:32 -0400839 }
Yi Tseng4f2a0462017-08-31 11:21:00 -0700840 }
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700841
rsahot036620655b2018-02-26 15:01:37 -0500842 // Remove broadcast flag
843 dhcpPacket.setFlags((short) 0);
844
845 udpPacket.setPayload(dhcpPacket);
846 // As a DHCP relay, the source port should be server port( instead
847 // of client port.
848 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
849 udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
850 ipv4Packet.setPayload(udpPacket);
851 ipv4Packet.setTtl((byte) 64);
852 etherReply.setPayload(ipv4Packet);
853 InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
854 serverInfo.getDhcpServerConnectPoint().get());
855 internalPackets.add(internalPacket);
856 }
857 return internalPackets;
Yi Tseng51301292017-07-28 13:02:59 -0700858 }
859
Charles Chand0dd7002017-10-08 23:53:36 -0400860
861 /**
862 * Do a basic routing for a packet from client (used for LQ processing).
863 *
864 * @param context the packet context
865 * @param ethernetPacket the ethernet payload to process
866 * @return processed packet
867 */
rsahot036620655b2018-02-26 15:01:37 -0500868 private List<InternalPacket> processLeaseQueryFromAgent(PacketContext context,
869 Ethernet ethernetPacket) {
870 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
871 DeviceId receivedFromDevice = receivedFrom.deviceId();
872
Charles Chand0dd7002017-10-08 23:53:36 -0400873 // get dhcp header.
874 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
875 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
876 UDP udpPacket = (UDP) ipv4Packet.getPayload();
877 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
878
Charles Chan2de55302018-03-02 18:27:59 -0800879 Ip4Address relayAgentIp;
Charles Chand0dd7002017-10-08 23:53:36 -0400880
Charles Chand0dd7002017-10-08 23:53:36 -0400881 Ip4Address clientInterfaceIp =
882 interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
883 .stream()
884 .map(Interface::ipAddressesList)
885 .flatMap(Collection::stream)
886 .map(InterfaceIpAddress::ipAddress)
887 .filter(IpAddress::isIp4)
888 .map(IpAddress::getIp4Address)
889 .findFirst()
890 .orElse(null);
891 if (clientInterfaceIp == null) {
892 log.warn("Can't find interface IP for client interface for port {}",
rsahot036620655b2018-02-26 15:01:37 -0500893 context.inPacket().receivedFrom());
Charles Chand0dd7002017-10-08 23:53:36 -0400894 return null;
895 }
rsahot036620655b2018-02-26 15:01:37 -0500896
Charles Chand0dd7002017-10-08 23:53:36 -0400897 boolean isDirectlyConnected = directlyConnected(dhcpPacket);
rsahot036620655b2018-02-26 15:01:37 -0500898 boolean directConnFlag = directlyConnected(dhcpPacket);
899
900 // Multi DHCP Start
901 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
902 VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
rsahot036620655b2018-02-26 15:01:37 -0500903
904 List<InternalPacket> internalPackets = new ArrayList<>();
905 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
Charles Chan2de55302018-03-02 18:27:59 -0800906 List<DhcpServerInfo> copyServerInfoList = new ArrayList<>(serverInfoList);
rsahot036620655b2018-02-26 15:01:37 -0500907
908 for (DhcpServerInfo serverInfo : copyServerInfoList) {
909 // get dhcp header.
910 etherReply = (Ethernet) ethernetPacket.clone();
911 ipv4Packet = (IPv4) etherReply.getPayload();
912 udpPacket = (UDP) ipv4Packet.getPayload();
913 dhcpPacket = (DHCP) udpPacket.getPayload();
914
915 if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
916 log.warn("Can't get server connect point, ignore");
917 continue;
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800918 }
rsahot036620655b2018-02-26 15:01:37 -0500919 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
920 if (newServerInfo == null) {
921 log.warn("Can't get server interface with host info resolved, ignore");
922 continue;
923 }
Charles Chand0dd7002017-10-08 23:53:36 -0400924
rsahot036620655b2018-02-26 15:01:37 -0500925 Interface serverInterface = getServerInterface(newServerInfo);
926 if (serverInterface == null) {
927 log.warn("Can't get server interface, ignore");
928 continue;
929 }
930 Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
931 MacAddress macFacingServer = serverInterface.mac();
932 if (ipFacingServer == null || macFacingServer == null) {
933 log.warn("No IP address for server Interface {}", serverInterface);
934 continue;
935 }
Charles Chand0dd7002017-10-08 23:53:36 -0400936
rsahot036620655b2018-02-26 15:01:37 -0500937 etherReply.setSourceMACAddress(macFacingServer);
Charles Chan2de55302018-03-02 18:27:59 -0800938 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
939 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
rsahot036620655b2018-02-26 15:01:37 -0500940 ipv4Packet.setSourceAddress(ipFacingServer.toInt());
Charles Chan2de55302018-03-02 18:27:59 -0800941 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
rsahot036620655b2018-02-26 15:01:37 -0500942 if (isDirectlyConnected) {
943 // set default info and replace with indirect if available later on.
944 if (newServerInfo.getDhcpConnectMac().isPresent()) {
945 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
946 }
947 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
948 etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
949 }
950 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
951 // Sets relay agent IP
952 int effectiveRelayAgentIp = relayAgentIp != null ?
953 relayAgentIp.toInt() : clientInterfaceIp.toInt();
954 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
955 } else {
956 if (!newServerInfo.getDhcpServerIp4().isPresent()) {
957 //do nothing
958 } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
959 continue;
960 } else {
961 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
962 // Sets relay agent IP
963 int effectiveRelayAgentIp = relayAgentIp != null ?
964 relayAgentIp.toInt() : clientInterfaceIp.toInt();
965 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
966 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
967 log.info("Relay Agent IP {}", relayAgentIp);
968 }
969
970 log.info("In Direct");
971 }
972
973 // Remove broadcast flag
974 dhcpPacket.setFlags((short) 0);
975
976 udpPacket.setPayload(dhcpPacket);
977 // As a DHCP relay, the source port should be server port( instead
978 // of client port.
979 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
980 udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
981 ipv4Packet.setPayload(udpPacket);
982 ipv4Packet.setTtl((byte) 64);
983 etherReply.setPayload(ipv4Packet);
984 ////return etherReply;
985 Dhcp4HandlerUtil.InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
986 newServerInfo.getDhcpServerConnectPoint().get());
987 internalPackets.add(internalPacket);
Charles Chand0dd7002017-10-08 23:53:36 -0400988 }
rsahot036620655b2018-02-26 15:01:37 -0500989 log.warn("num of processLeaseQueryFromAgent packets to send is{}", internalPackets.size());
Charles Chand0dd7002017-10-08 23:53:36 -0400990
rsahot036620655b2018-02-26 15:01:37 -0500991 return internalPackets;
Charles Chand0dd7002017-10-08 23:53:36 -0400992 }
993
994
Yi Tseng51301292017-07-28 13:02:59 -0700995 /**
996 * Writes DHCP record to the store according to the request DHCP packet (Discover, Request).
997 *
998 * @param location the location which DHCP packet comes from
999 * @param ethernet the DHCP packet
1000 * @param dhcpPayload the DHCP payload
1001 */
1002 private void writeRequestDhcpRecord(ConnectPoint location,
1003 Ethernet ethernet,
1004 DHCP dhcpPayload) {
1005 VlanId vlanId = VlanId.vlanId(ethernet.getVlanID());
1006 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1007 HostId hostId = HostId.hostId(macAddress, vlanId);
1008 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1009 if (record == null) {
1010 record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
1011 } else {
1012 record = record.clone();
1013 }
1014 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
1015 record.ip4Status(dhcpPayload.getPacketType());
1016 record.setDirectlyConnected(directlyConnected(dhcpPayload));
1017 if (!directlyConnected(dhcpPayload)) {
1018 // Update gateway mac address if the host is not directly connected
1019 record.nextHop(ethernet.getSourceMAC());
1020 }
1021 record.updateLastSeen();
1022 dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
1023 }
1024
1025 /**
1026 * Writes DHCP record to the store according to the response DHCP packet (Offer, Ack).
1027 *
1028 * @param ethernet the DHCP packet
1029 * @param dhcpPayload the DHCP payload
1030 */
1031 private void writeResponseDhcpRecord(Ethernet ethernet,
1032 DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001033 Optional<Interface> outInterface = getClientInterface(ethernet, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -07001034 if (!outInterface.isPresent()) {
1035 log.warn("Failed to determine where to send {}", dhcpPayload.getPacketType());
1036 return;
1037 }
1038
1039 Interface outIface = outInterface.get();
1040 ConnectPoint location = outIface.connectPoint();
Yi Tseng4f2a0462017-08-31 11:21:00 -07001041 VlanId vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001042 if (vlanId == null) {
1043 vlanId = outIface.vlan();
1044 }
Yi Tseng51301292017-07-28 13:02:59 -07001045 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1046 HostId hostId = HostId.hostId(macAddress, vlanId);
1047 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1048 if (record == null) {
1049 record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
1050 } else {
1051 record = record.clone();
1052 }
1053 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
1054 if (dhcpPayload.getPacketType() == DHCP.MsgType.DHCPACK) {
1055 record.ip4Address(Ip4Address.valueOf(dhcpPayload.getYourIPAddress()));
1056 }
1057 record.ip4Status(dhcpPayload.getPacketType());
1058 record.setDirectlyConnected(directlyConnected(dhcpPayload));
1059 record.updateLastSeen();
1060 dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
1061 }
1062
1063 /**
1064 * Build the DHCP offer/ack with proper client port.
1065 *
1066 * @param ethernetPacket the original packet comes from server
1067 * @return new packet which will send to the client
1068 */
rsahot036620655b2018-02-26 15:01:37 -05001069 private Ethernet processDhcpPacketFromServer(PacketContext context, Ethernet ethernetPacket) {
Yi Tseng51301292017-07-28 13:02:59 -07001070 // get dhcp header.
rsahot036620655b2018-02-26 15:01:37 -05001071 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Yi Tseng51301292017-07-28 13:02:59 -07001072 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
1073 UDP udpPacket = (UDP) ipv4Packet.getPayload();
1074 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
1075
1076 // determine the vlanId of the client host - note that this vlan id
1077 // could be different from the vlan in the packet from the server
Yi Tsengdcef2c22017-08-05 20:34:06 -07001078 Interface clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
Yi Tseng51301292017-07-28 13:02:59 -07001079
Yi Tsengdcef2c22017-08-05 20:34:06 -07001080 if (clientInterface == null) {
Yi Tseng51301292017-07-28 13:02:59 -07001081 log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
1082 return null;
1083 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001084 VlanId vlanId;
rsahot036620655b2018-02-26 15:01:37 -05001085 ConnectPoint inPort = context.inPacket().receivedFrom();
1086 boolean directConnFlag = directlyConnected(dhcpPayload);
1087 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
1088
1089 if (foundServerInfo == null) {
1090 log.warn("Cannot find server info");
1091 return null;
1092 } else {
1093 if (dhcp4HandlerUtil.isServerIpEmpty(foundServerInfo)) {
1094 log.warn("Cannot find server info's ipaddress");
1095 return null;
1096 }
1097 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001098 if (clientInterface.vlanTagged().isEmpty()) {
1099 vlanId = clientInterface.vlan();
1100 } else {
1101 // might be multiple vlan in same interface
Yi Tseng4f2a0462017-08-31 11:21:00 -07001102 vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001103 }
1104 if (vlanId == null) {
1105 vlanId = VlanId.NONE;
1106 }
1107 etherReply.setVlanID(vlanId.toShort());
1108 etherReply.setSourceMACAddress(clientInterface.mac());
Yi Tseng51301292017-07-28 13:02:59 -07001109
Yi Tsengdcef2c22017-08-05 20:34:06 -07001110 if (!directlyConnected(dhcpPayload)) {
1111 // if client is indirectly connected, try use next hop mac address
1112 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1113 HostId hostId = HostId.hostId(macAddress, vlanId);
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001114 if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
1115 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1116 if (record != null) {
1117 // if next hop can be found, use mac address of next hop
1118 record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
1119 } else {
1120 // otherwise, discard the packet
1121 log.warn("Can't find record for host id {}, discard packet", hostId);
1122 return null;
1123 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001124 } else {
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001125 etherReply.setDestinationMACAddress(MacAddress.BROADCAST);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001126 }
Yi Tseng1696f562017-08-17 17:43:38 -07001127 } else {
1128 etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
Yi Tsengdcef2c22017-08-05 20:34:06 -07001129 }
1130
Yi Tseng51301292017-07-28 13:02:59 -07001131 // we leave the srcMac from the original packet
Yi Tseng51301292017-07-28 13:02:59 -07001132 // figure out the relay agent IP corresponding to the original request
Yi Tseng3df7f9d2017-08-17 13:08:31 -07001133 Ip4Address ipFacingClient = getFirstIpFromInterface(clientInterface);
1134 if (ipFacingClient == null) {
Yi Tseng51301292017-07-28 13:02:59 -07001135 log.warn("Cannot determine relay agent interface Ipv4 addr for host {}/{}. "
1136 + "Aborting relay for dhcp packet from server {}",
Yi Tsengdcef2c22017-08-05 20:34:06 -07001137 etherReply.getDestinationMAC(), clientInterface.vlan(),
Yi Tseng51301292017-07-28 13:02:59 -07001138 ethernetPacket);
1139 return null;
1140 }
1141 // SRC_IP: relay agent IP
1142 // DST_IP: offered IP
Yi Tseng3df7f9d2017-08-17 13:08:31 -07001143 ipv4Packet.setSourceAddress(ipFacingClient.toInt());
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001144 if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
1145 ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
1146 } else {
1147 ipv4Packet.setDestinationAddress(BROADCAST_IP);
1148 }
Yi Tseng51301292017-07-28 13:02:59 -07001149 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
1150 if (directlyConnected(dhcpPayload)) {
1151 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
1152 } else {
1153 // forward to another dhcp relay
Yi Tseng93ba53c2017-09-14 13:24:21 -07001154 // FIXME: Currently we assume the DHCP comes from a L2 relay with
1155 // Option 82, this might not work if DHCP message comes from
1156 // L3 relay.
1157 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
Yi Tseng51301292017-07-28 13:02:59 -07001158 }
1159
1160 udpPacket.setPayload(dhcpPayload);
1161 ipv4Packet.setPayload(udpPacket);
1162 etherReply.setPayload(ipv4Packet);
1163 return etherReply;
1164 }
1165
Yi Tsengdcef2c22017-08-05 20:34:06 -07001166 /**
Charles Chand0dd7002017-10-08 23:53:36 -04001167 * Build the DHCP offer/ack with proper client port.
1168 *
1169 * @param ethernetPacket the original packet comes from server
1170 * @return new packet which will send to the client
1171 */
1172 private Ethernet processLeaseQueryFromServer(Ethernet ethernetPacket) {
1173 // get dhcp header.
1174 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
1175 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
1176 UDP udpPacket = (UDP) ipv4Packet.getPayload();
1177 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
1178
1179 // determine the vlanId of the client host - note that this vlan id
1180 // could be different from the vlan in the packet from the server
1181 Interface clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
1182
1183 if (clientInterface == null) {
1184 log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
1185 return null;
1186 }
1187 VlanId vlanId;
1188 if (clientInterface.vlanTagged().isEmpty()) {
1189 vlanId = clientInterface.vlan();
1190 } else {
1191 // might be multiple vlan in same interface
1192 vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
1193 }
1194 if (vlanId == null) {
1195 vlanId = VlanId.NONE;
1196 }
1197 etherReply.setVlanID(vlanId.toShort());
1198 etherReply.setSourceMACAddress(clientInterface.mac());
1199
1200 if (!directlyConnected(dhcpPayload)) {
1201 // if client is indirectly connected, try use next hop mac address
1202 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1203 HostId hostId = HostId.hostId(macAddress, vlanId);
1204 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1205 if (record != null) {
1206 // if next hop can be found, use mac address of next hop
1207 record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
1208 } else {
1209 // otherwise, discard the packet
1210 log.warn("Can't find record for host id {}, discard packet", hostId);
1211 return null;
1212 }
1213 } else {
1214 etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
1215 }
1216
1217 // default is client port
1218 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
1219 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
1220
1221 udpPacket.setPayload(dhcpPayload);
1222 ipv4Packet.setPayload(udpPacket);
1223 etherReply.setPayload(ipv4Packet);
1224 return etherReply;
1225 }
1226 /**
Yi Tsengdcef2c22017-08-05 20:34:06 -07001227 * Extracts VLAN ID from relay agent option.
1228 *
1229 * @param dhcpPayload the DHCP payload
1230 * @return VLAN ID from DHCP payload; null if not exists
1231 */
Yi Tseng4f2a0462017-08-31 11:21:00 -07001232 private VlanId getVlanIdFromRelayAgentOption(DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001233 DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
1234 if (option == null) {
1235 return null;
1236 }
1237 DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
1238 if (circuitIdSubOption == null) {
1239 return null;
1240 }
1241 try {
1242 CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
1243 return circuitId.vlanId();
1244 } catch (IllegalArgumentException e) {
1245 // can't deserialize the circuit ID
1246 return null;
1247 }
1248 }
1249
1250 /**
1251 * Removes DHCP relay agent information option (option 82) from DHCP payload.
1252 * Also reset giaddr to 0
1253 *
1254 * @param ethPacket the Ethernet packet to be processed
1255 * @return Ethernet packet processed
1256 */
1257 private Ethernet removeRelayAgentOption(Ethernet ethPacket) {
rsahot036620655b2018-02-26 15:01:37 -05001258 Ethernet ethernet = (Ethernet) ethPacket.duplicate();
Yi Tsengdcef2c22017-08-05 20:34:06 -07001259 IPv4 ipv4 = (IPv4) ethernet.getPayload();
1260 UDP udp = (UDP) ipv4.getPayload();
1261 DHCP dhcpPayload = (DHCP) udp.getPayload();
1262
1263 // removes relay agent information option
1264 List<DhcpOption> options = dhcpPayload.getOptions();
1265 options = options.stream()
1266 .filter(option -> option.getCode() != OptionCode_CircuitID.getValue())
1267 .collect(Collectors.toList());
1268 dhcpPayload.setOptions(options);
1269 dhcpPayload.setGatewayIPAddress(0);
1270
1271 udp.setPayload(dhcpPayload);
1272 ipv4.setPayload(udp);
1273 ethernet.setPayload(ipv4);
1274 return ethernet;
1275 }
1276
Yi Tseng51301292017-07-28 13:02:59 -07001277
1278 /**
1279 * Check if the host is directly connected to the network or not.
1280 *
1281 * @param dhcpPayload the dhcp payload
1282 * @return true if the host is directly connected to the network; false otherwise
1283 */
1284 private boolean directlyConnected(DHCP dhcpPayload) {
Yi Tseng440e2b72017-08-24 14:47:34 -07001285 DhcpRelayAgentOption relayAgentOption =
1286 (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
Yi Tseng51301292017-07-28 13:02:59 -07001287
1288 // Doesn't contains relay option
1289 if (relayAgentOption == null) {
1290 return true;
1291 }
1292
Yi Tseng440e2b72017-08-24 14:47:34 -07001293 // check circuit id, if circuit id is invalid, we say it is an indirect host
1294 DhcpOption circuitIdOpt = relayAgentOption.getSubOption(CIRCUIT_ID.getValue());
Yi Tseng51301292017-07-28 13:02:59 -07001295
Yi Tseng440e2b72017-08-24 14:47:34 -07001296 try {
1297 CircuitId.deserialize(circuitIdOpt.getData());
Yi Tseng51301292017-07-28 13:02:59 -07001298 return true;
Yi Tseng440e2b72017-08-24 14:47:34 -07001299 } catch (Exception e) {
1300 // invalid circuit id
1301 return false;
Yi Tseng51301292017-07-28 13:02:59 -07001302 }
Yi Tseng51301292017-07-28 13:02:59 -07001303 }
1304
1305
1306 /**
1307 * Send the DHCP ack to the requester host.
1308 * Modify Host or Route store according to the type of DHCP.
1309 *
1310 * @param ethernetPacketAck the packet
1311 * @param dhcpPayload the DHCP data
1312 */
1313 private void handleDhcpAck(Ethernet ethernetPacketAck, DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001314 Optional<Interface> outInterface = getClientInterface(ethernetPacketAck, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -07001315 if (!outInterface.isPresent()) {
1316 log.warn("Can't find output interface for dhcp: {}", dhcpPayload);
1317 return;
1318 }
1319
1320 Interface outIface = outInterface.get();
1321 HostLocation hostLocation = new HostLocation(outIface.connectPoint(), System.currentTimeMillis());
1322 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
Yi Tseng4f2a0462017-08-31 11:21:00 -07001323 VlanId vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001324 if (vlanId == null) {
1325 vlanId = outIface.vlan();
1326 }
Yi Tseng51301292017-07-28 13:02:59 -07001327 HostId hostId = HostId.hostId(macAddress, vlanId);
1328 Ip4Address ip = Ip4Address.valueOf(dhcpPayload.getYourIPAddress());
1329
1330 if (directlyConnected(dhcpPayload)) {
1331 // Add to host store if it connect to network directly
1332 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tsengaa417a62017-09-08 17:22:51 -07001333 Host host = hostService.getHost(hostId);
Yi Tseng51301292017-07-28 13:02:59 -07001334
Yi Tsengaa417a62017-09-08 17:22:51 -07001335 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
1336 if (host != null) {
1337 // Dual homing support:
1338 // if host exists, use old locations and new location
1339 hostLocations.addAll(host.locations());
1340 }
1341 HostDescription desc = new DefaultHostDescription(macAddress, vlanId,
1342 hostLocations, ips, false);
1343 // Add IP address when dhcp server give the host new ip address
1344 providerService.hostDetected(hostId, desc, false);
Yi Tseng51301292017-07-28 13:02:59 -07001345 } else {
1346 // Add to route store if it does not connect to network directly
1347 // Get gateway host IP according to host mac address
Yi Tsengdcef2c22017-08-05 20:34:06 -07001348 // TODO: remove relay store here
Yi Tseng51301292017-07-28 13:02:59 -07001349 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1350
1351 if (record == null) {
1352 log.warn("Can't find DHCP record of host {}", hostId);
1353 return;
1354 }
1355
1356 MacAddress gwMac = record.nextHop().orElse(null);
1357 if (gwMac == null) {
1358 log.warn("Can't find gateway mac address from record {}", record);
1359 return;
1360 }
1361
1362 HostId gwHostId = HostId.hostId(gwMac, record.vlanId());
1363 Host gwHost = hostService.getHost(gwHostId);
1364
1365 if (gwHost == null) {
1366 log.warn("Can't find gateway host {}", gwHostId);
1367 return;
1368 }
1369
1370 Ip4Address nextHopIp = gwHost.ipAddresses()
1371 .stream()
1372 .filter(IpAddress::isIp4)
1373 .map(IpAddress::getIp4Address)
1374 .findFirst()
1375 .orElse(null);
1376
1377 if (nextHopIp == null) {
1378 log.warn("Can't find IP address of gateway {}", gwHost);
1379 return;
1380 }
1381
Charles Chan6305b692018-04-04 11:43:54 -07001382 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Yi Tseng51301292017-07-28 13:02:59 -07001383 routeStore.updateRoute(route);
1384 }
Yi Tseng51301292017-07-28 13:02:59 -07001385 }
1386
1387 /**
Yi Tseng51301292017-07-28 13:02:59 -07001388 * Gets output interface of a dhcp packet.
1389 * If option 82 exists in the dhcp packet and the option was sent by
Yi Tseng4f2a0462017-08-31 11:21:00 -07001390 * ONOS (circuit format is correct), use the connect
Yi Tseng51301292017-07-28 13:02:59 -07001391 * point and vlan id from circuit id; otherwise, find host by destination
1392 * address and use vlan id from sender (dhcp server).
1393 *
1394 * @param ethPacket the ethernet packet
1395 * @param dhcpPayload the dhcp packet
1396 * @return an interface represent the output port and vlan; empty value
1397 * if the host or circuit id not found
1398 */
Yi Tsengdcef2c22017-08-05 20:34:06 -07001399 private Optional<Interface> getClientInterface(Ethernet ethPacket, DHCP dhcpPayload) {
Yi Tseng51301292017-07-28 13:02:59 -07001400 VlanId originalPacketVlanId = VlanId.vlanId(ethPacket.getVlanID());
Yi Tseng51301292017-07-28 13:02:59 -07001401 DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
1402
Yi Tseng4f2a0462017-08-31 11:21:00 -07001403 DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
1404 try {
1405 CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
1406 ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(circuitId.connectPoint());
1407 VlanId vlanId = circuitId.vlanId();
1408 return interfaceService.getInterfacesByPort(connectPoint)
1409 .stream()
1410 .filter(iface -> interfaceContainsVlan(iface, vlanId))
1411 .findFirst();
1412 } catch (IllegalArgumentException ex) {
1413 // invalid circuit format, didn't sent by ONOS
1414 log.debug("Invalid circuit {}, use information from dhcp payload",
1415 circuitIdSubOption.getData());
Yi Tseng51301292017-07-28 13:02:59 -07001416 }
1417
1418 // Use Vlan Id from DHCP server if DHCP relay circuit id was not
1419 // sent by ONOS or circuit Id can't be parsed
Yi Tsengdcef2c22017-08-05 20:34:06 -07001420 // TODO: remove relay store from this method
Yi Tseng51301292017-07-28 13:02:59 -07001421 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
1422 Optional<DhcpRecord> dhcpRecord = dhcpRelayStore.getDhcpRecord(HostId.hostId(dstMac, originalPacketVlanId));
Yi Tsengdcef2c22017-08-05 20:34:06 -07001423 ConnectPoint clientConnectPoint = dhcpRecord
Yi Tseng51301292017-07-28 13:02:59 -07001424 .map(DhcpRecord::locations)
1425 .orElse(Collections.emptySet())
1426 .stream()
1427 .reduce((hl1, hl2) -> {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001428 // find latest host connect point
Yi Tseng51301292017-07-28 13:02:59 -07001429 if (hl1 == null || hl2 == null) {
1430 return hl1 == null ? hl2 : hl1;
1431 }
1432 return hl1.time() > hl2.time() ? hl1 : hl2;
1433 })
Yi Tsengdcef2c22017-08-05 20:34:06 -07001434 .orElse(null);
Yi Tseng51301292017-07-28 13:02:59 -07001435
Yi Tsengdcef2c22017-08-05 20:34:06 -07001436 if (clientConnectPoint != null) {
1437 return interfaceService.getInterfacesByPort(clientConnectPoint)
1438 .stream()
Yi Tseng4f2a0462017-08-31 11:21:00 -07001439 .filter(iface -> interfaceContainsVlan(iface, originalPacketVlanId))
Yi Tsengdcef2c22017-08-05 20:34:06 -07001440 .findFirst();
1441 }
1442 return Optional.empty();
Yi Tseng51301292017-07-28 13:02:59 -07001443 }
1444
1445 /**
1446 * Send the response DHCP to the requester host.
1447 *
1448 * @param ethPacket the packet
1449 * @param dhcpPayload the DHCP data
1450 */
1451 private void sendResponseToClient(Ethernet ethPacket, DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001452 Optional<Interface> outInterface = getClientInterface(ethPacket, dhcpPayload);
1453 if (directlyConnected(dhcpPayload)) {
1454 ethPacket = removeRelayAgentOption(ethPacket);
1455 }
1456 if (!outInterface.isPresent()) {
1457 log.warn("Can't find output interface for client, ignore");
1458 return;
1459 }
1460 Interface outIface = outInterface.get();
1461 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1462 .setOutput(outIface.connectPoint().port())
1463 .build();
1464 OutboundPacket o = new DefaultOutboundPacket(
1465 outIface.connectPoint().deviceId(),
1466 treatment,
1467 ByteBuffer.wrap(ethPacket.serialize()));
1468 if (log.isTraceEnabled()) {
1469 log.trace("Relaying packet to DHCP client {} via {}, vlan {}",
1470 ethPacket,
1471 outIface.connectPoint(),
1472 outIface.vlan());
1473 }
1474 packetService.emit(o);
Yi Tseng51301292017-07-28 13:02:59 -07001475 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001476
Yi Tsengaa417a62017-09-08 17:22:51 -07001477 @Override
1478 public void triggerProbe(Host host) {
1479 // Do nothing here
1480 }
1481
1482 @Override
1483 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -07001484 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -07001485 }
1486
Yi Tseng483ac6f2017-08-02 15:03:31 -07001487 class InternalHostListener implements HostListener {
1488 @Override
1489 public void event(HostEvent event) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001490 if (!configured()) {
1491 return;
1492 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001493 switch (event.type()) {
1494 case HOST_ADDED:
1495 case HOST_UPDATED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001496 log.trace("Scheduled host event {}", event);
1497 hostEventExecutor.execute(() -> hostUpdated(event.subject()));
Yi Tseng483ac6f2017-08-02 15:03:31 -07001498 break;
1499 case HOST_REMOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001500 log.trace("Scheduled host event {}", event);
1501 hostEventExecutor.execute(() -> hostRemoved(event.subject()));
Yi Tseng483ac6f2017-08-02 15:03:31 -07001502 break;
Yi Tseng483ac6f2017-08-02 15:03:31 -07001503 default:
1504 break;
1505 }
1506 }
1507 }
1508
1509 /**
Yi Tseng483ac6f2017-08-02 15:03:31 -07001510 * Handle host updated.
1511 * If the host is DHCP server or gateway, update connect mac and vlan.
1512 *
1513 * @param host the host
1514 */
1515 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001516 hostUpdated(host, defaultServerInfoList);
1517 hostUpdated(host, indirectServerInfoList);
Yi Tseng483ac6f2017-08-02 15:03:31 -07001518 }
1519
Yi Tseng525ff402017-10-23 19:39:39 -07001520 private void hostUpdated(Host host, List<DhcpServerInfo> srverInfoList) {
1521 DhcpServerInfo serverInfo;
1522 Ip4Address targetIp;
1523 if (!srverInfoList.isEmpty()) {
1524 serverInfo = srverInfoList.get(0);
1525 targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
1526 Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
1527
1528 if (targetIp == null) {
1529 targetIp = serverIp;
1530 }
1531
1532 if (targetIp != null) {
1533 if (host.ipAddresses().contains(targetIp)) {
1534 serverInfo.setDhcpConnectMac(host.mac());
1535 serverInfo.setDhcpConnectVlan(host.vlan());
1536 requestDhcpPacket(serverIp);
1537 }
1538 }
1539 }
1540 }
1541
1542
Yi Tseng483ac6f2017-08-02 15:03:31 -07001543 /**
1544 * Handle host removed.
1545 * If the host is DHCP server or gateway, unset connect mac and vlan.
1546 *
1547 * @param host the host
1548 */
1549 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001550 hostRemoved(host, defaultServerInfoList);
1551 hostRemoved(host, indirectServerInfoList);
1552 }
1553
1554 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001555 DhcpServerInfo serverInfo;
Yi Tseng525ff402017-10-23 19:39:39 -07001556 Ip4Address targetIp;
1557 if (!serverInfoList.isEmpty()) {
1558 serverInfo = serverInfoList.get(0);
1559 Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
1560 targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
Yi Tseng919b2df2017-09-07 16:22:51 -07001561
Yi Tseng525ff402017-10-23 19:39:39 -07001562 if (targetIp == null) {
1563 targetIp = serverIp;
Yi Tseng919b2df2017-09-07 16:22:51 -07001564 }
Yi Tseng525ff402017-10-23 19:39:39 -07001565
1566 if (targetIp != null) {
1567 if (host.ipAddresses().contains(targetIp)) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001568 serverInfo.setDhcpConnectVlan(null);
1569 serverInfo.setDhcpConnectMac(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001570 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001571 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001572 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001573 }
Yi Tseng525ff402017-10-23 19:39:39 -07001574 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001575
Yi Tseng525ff402017-10-23 19:39:39 -07001576 private void requestDhcpPacket(Ip4Address serverIp) {
1577 requestServerDhcpPacket(serverIp);
1578 requestClientDhcpPacket(serverIp);
1579 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001580
Yi Tseng525ff402017-10-23 19:39:39 -07001581 private void cancelDhcpPacket(Ip4Address serverIp) {
1582 cancelServerDhcpPacket(serverIp);
1583 cancelClientDhcpPacket(serverIp);
1584 }
1585
1586 private void cancelServerDhcpPacket(Ip4Address serverIp) {
1587 TrafficSelector serverSelector =
1588 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1589 .matchIPSrc(serverIp.toIpPrefix())
1590 .build();
1591 packetService.cancelPackets(serverSelector,
1592 PacketPriority.CONTROL,
1593 appId);
1594 }
1595
1596 private void requestServerDhcpPacket(Ip4Address serverIp) {
1597 TrafficSelector serverSelector =
1598 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1599 .matchIPSrc(serverIp.toIpPrefix())
1600 .build();
1601 packetService.requestPackets(serverSelector,
1602 PacketPriority.CONTROL,
1603 appId);
1604 }
1605
1606 private void cancelClientDhcpPacket(Ip4Address serverIp) {
1607 // Packet comes from relay
1608 TrafficSelector indirectClientSelector =
1609 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1610 .matchIPDst(serverIp.toIpPrefix())
1611 .build();
1612 packetService.cancelPackets(indirectClientSelector,
1613 PacketPriority.CONTROL,
1614 appId);
1615
1616 // Packet comes from client
1617 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1618 PacketPriority.CONTROL,
1619 appId);
1620 }
1621
1622 private void requestClientDhcpPacket(Ip4Address serverIp) {
1623 // Packet comes from relay
1624 TrafficSelector indirectClientSelector =
1625 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1626 .matchIPDst(serverIp.toIpPrefix())
1627 .build();
1628 packetService.requestPackets(indirectClientSelector,
1629 PacketPriority.CONTROL,
1630 appId);
1631
1632 // Packet comes from client
1633 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1634 PacketPriority.CONTROL,
1635 appId);
1636 }
1637
1638 /**
1639 * Process the ignore rules.
1640 *
1641 * @param deviceId the device id
1642 * @param vlanId the vlan to be ignored
1643 * @param op the operation, ADD to install; REMOVE to uninstall rules
1644 */
1645 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001646 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1647 DHCP_SELECTORS.forEach(trafficSelector -> {
1648 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1649 .matchVlanId(vlanId)
1650 .build();
1651
1652 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1653 .withFlag(ForwardingObjective.Flag.VERSATILE)
1654 .withSelector(selector)
1655 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001656 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001657 .fromApp(appId);
1658
1659
1660 ObjectiveContext objectiveContext = new ObjectiveContext() {
1661 @Override
1662 public void onSuccess(Objective objective) {
1663 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1664 op, vlanId, deviceId, selector);
1665 int countDown = installedCount.decrementAndGet();
1666 if (countDown != 0) {
1667 return;
1668 }
1669 switch (op) {
1670 case ADD:
1671 ignoredVlans.put(deviceId, vlanId);
1672 break;
1673 case REMOVE:
1674 ignoredVlans.remove(deviceId, vlanId);
1675 break;
1676 default:
1677 log.warn("Unsupported objective operation {}", op);
1678 break;
1679 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001680 }
Yi Tseng525ff402017-10-23 19:39:39 -07001681
1682 @Override
1683 public void onError(Objective objective, ObjectiveError error) {
1684 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1685 op, vlanId, selector, deviceId, error);
Yi Tseng919b2df2017-09-07 16:22:51 -07001686 }
Yi Tseng525ff402017-10-23 19:39:39 -07001687 };
1688
1689 ForwardingObjective fwd;
1690 switch (op) {
1691 case ADD:
1692 fwd = builder.add(objectiveContext);
1693 break;
1694 case REMOVE:
1695 fwd = builder.remove(objectiveContext);
1696 break;
1697 default:
1698 log.warn("Unsupported objective operation {}", op);
1699 return;
Yi Tseng4f2a0462017-08-31 11:21:00 -07001700 }
Yi Tseng525ff402017-10-23 19:39:39 -07001701
1702 Device device = deviceService.getDevice(deviceId);
1703 if (device == null || !device.is(Pipeliner.class)) {
1704 log.warn("Device {} is not available now, wait until device is available", deviceId);
1705 return;
1706 }
1707 flowObjectiveService.apply(deviceId, fwd);
1708 });
Yi Tseng483ac6f2017-08-02 15:03:31 -07001709 }
Kalhee Kimba366062017-11-07 16:32:09 +00001710
1711 @Override
1712 public void setDhcpFpmEnabled(Boolean enabled) {
1713 // v4 does not use fpm. Do nothing.
1714 }
rsahot036620655b2018-02-26 15:01:37 -05001715 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1716 List<DhcpServerInfo> validServerInfo;
1717
1718 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1719 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1720 } else {
1721 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1722 }
1723 return validServerInfo;
1724 }
1725
1726
1727 private boolean checkDhcpServerConnPt(boolean directConnFlag,
1728 DhcpServerInfo serverInfo) {
1729 if (serverInfo.getDhcpServerConnectPoint() == null) {
1730 log.warn("DHCP4 server connect point for {} connPt {}",
1731 directConnFlag ? "direct" : "indirect", serverInfo.getDhcpServerConnectPoint());
1732 return false;
1733 }
1734 return true;
1735 }
1736
1737 /**
1738 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1739 *
1740 * @param serverInfo server information
1741 * @return newServerInfo if host info can be either found or filled in.
1742 */
1743 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1744 DhcpServerInfo newServerInfo = null;
1745 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1746 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1747 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1748
1749 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1750 newServerInfo = serverInfo;
Charles Chan2de55302018-03-02 18:27:59 -08001751 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp4(),
rsahot036620655b2018-02-26 15:01:37 -05001752 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1753 } else {
1754 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp4(),
1755 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1756
1757 Ip4Address ipToProbe;
1758 if (serverInfo.getDhcpGatewayIp4().isPresent()) {
1759 ipToProbe = serverInfo.getDhcpGatewayIp4().get();
1760 } else {
1761 ipToProbe = serverInfo.getDhcpServerIp4().orElse(null);
1762 }
1763 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1764 .map(ip -> "gateway").orElse("server");
1765
1766 log.warn("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1767 hostService.startMonitoringIp(ipToProbe);
1768
1769 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1770 if (!hosts.isEmpty()) {
1771 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1772 Host host = hosts.iterator().next();
1773 serverInfo.setDhcpConnectVlan(host.vlan());
1774 serverInfo.setDhcpConnectMac(host.mac());
1775 // replace the serverInfo in the list
1776 sererInfoList.set(serverInfoIndex, serverInfo);
1777 newServerInfo = serverInfo;
1778 log.warn("Dynamically host found host {}", host);
1779 } else {
1780 log.warn("No host found host ip {} dynamically", ipToProbe);
1781 }
1782 }
1783 return newServerInfo;
1784 }
1785
1786 /**
1787 * Gets Interface facing to the server for default host.
1788 *
1789 * @param serverInfo server information
1790 * @return the Interface facing to the server; null if not found
1791 */
1792 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1793 Interface serverInterface = null;
1794
1795 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1796 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1797
1798 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1799 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1800 .stream()
1801 .filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1802 .findFirst()
1803 .orElse(null);
1804 } else {
1805 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1806 dhcpServerConnectPoint, dhcpConnectVlan);
1807 }
1808
1809 return serverInterface;
1810 }
1811
1812 //forward the packet to ConnectPoint where the DHCP server is attached.
1813 private void forwardPacket(InternalPacket packet) {
1814 //send Packetout to dhcp server connectpoint.
1815 if (packet.destLocation != null) {
1816 TrafficTreatment t = DefaultTrafficTreatment.builder()
1817 .setOutput(packet.destLocation.port()).build();
1818 OutboundPacket o = new DefaultOutboundPacket(
1819 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
1820 if (log.isTraceEnabled()) {
1821 log.trace("Relaying packet to destination {}", packet.destLocation);
1822 }
Charles Chan2de55302018-03-02 18:27:59 -08001823 log.debug("packetService.emit(o) to port {}", packet.destLocation);
rsahot036620655b2018-02-26 15:01:37 -05001824 packetService.emit(o);
1825 }
1826 }
1827
1828
1829 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1830 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1831 DhcpServerInfo foundServerInfo = null;
1832 for (DhcpServerInfo serverInfo : validServerInfoList) {
1833 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1834 foundServerInfo = serverInfo;
Charles Chan2de55302018-03-02 18:27:59 -08001835 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
rsahot036620655b2018-02-26 15:01:37 -05001836 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1837 break;
1838 } else {
1839 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1840 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1841 }
1842 }
1843 return foundServerInfo;
1844 }
1845
Yi Tseng51301292017-07-28 13:02:59 -07001846}