blob: 2ec54af0f3b5b054834cc3f91fddbef5d2948aec [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;
Yi Tseng525ff402017-10-23 19:39:39 -0700102import java.util.concurrent.atomic.AtomicInteger;
Yi Tseng51301292017-07-28 13:02:59 -0700103import java.util.stream.Collectors;
104
105import static com.google.common.base.Preconditions.checkNotNull;
106import static com.google.common.base.Preconditions.checkState;
107import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
108import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
109import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
110import static org.onlab.packet.MacAddress.valueOf;
111import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID;
Yi Tseng525ff402017-10-23 19:39:39 -0700112import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
113import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700114
rsahot036620655b2018-02-26 15:01:37 -0500115import org.onosproject.dhcprelay.Dhcp4HandlerUtil.InternalPacket;
116
Yi Tseng51301292017-07-28 13:02:59 -0700117@Component
118@Service
119@Property(name = "version", value = "4")
Yi Tsengaa417a62017-09-08 17:22:51 -0700120public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700121 public static final String DHCP_V4_RELAY_APP = "org.onosproject.Dhcp4HandlerImpl";
122 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp4", DHCP_V4_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700123 private static final String BROADCAST_IP = "255.255.255.255";
124 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
125
126 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
127 .matchEthType(Ethernet.TYPE_IPV4)
128 .matchIPProtocol(IPv4.PROTOCOL_UDP)
129 .matchIPSrc(Ip4Address.ZERO.toIpPrefix())
130 .matchIPDst(Ip4Address.valueOf(BROADCAST_IP).toIpPrefix())
131 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
132 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
133 .build();
134 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
135 .matchEthType(Ethernet.TYPE_IPV4)
136 .matchIPProtocol(IPv4.PROTOCOL_UDP)
137 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
138 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
139 .build();
140 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
141 CLIENT_SERVER_SELECTOR,
142 SERVER_RELAY_SELECTOR
143 );
Yi Tseng51301292017-07-28 13:02:59 -0700144 private static Logger log = LoggerFactory.getLogger(Dhcp4HandlerImpl.class);
145
146 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
147 protected DhcpRelayStore dhcpRelayStore;
148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
150 protected PacketService packetService;
151
152 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng51301292017-07-28 13:02:59 -0700153 protected RouteStore routeStore;
154
155 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
156 protected InterfaceService interfaceService;
157
158 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
159 protected HostService hostService;
160
Yi Tsengaa417a62017-09-08 17:22:51 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
162 protected HostProviderRegistry providerRegistry;
163
Yi Tseng525ff402017-10-23 19:39:39 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
165 protected CoreService coreService;
166
167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
168 protected DeviceService deviceService;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
171 protected FlowObjectiveService flowObjectiveService;
172
Yi Tsengaa417a62017-09-08 17:22:51 -0700173 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700174 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800175 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng483ac6f2017-08-02 15:03:31 -0700176 private InternalHostListener hostListener = new InternalHostListener();
177
Charles Chan909cff82018-03-05 13:14:02 -0800178 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
179 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
rsahot036620655b2018-02-26 15:01:37 -0500180 private Dhcp4HandlerUtil dhcp4HandlerUtil = new Dhcp4HandlerUtil();
Yi Tseng4f2a0462017-08-31 11:21:00 -0700181
Yi Tseng483ac6f2017-08-02 15:03:31 -0700182 @Activate
183 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700184 appId = coreService.registerApplication(DHCP_V4_RELAY_APP);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700185 hostService.addListener(hostListener);
Yi Tsengaa417a62017-09-08 17:22:51 -0700186 providerService = providerRegistry.register(this);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700187 }
188
189 @Deactivate
190 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700191 providerRegistry.unregister(this);
192 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700193 defaultServerInfoList.forEach(this::stopMonitoringIps);
194 defaultServerInfoList.clear();
195 indirectServerInfoList.forEach(this::stopMonitoringIps);
196 indirectServerInfoList.clear();
Yi Tseng483ac6f2017-08-02 15:03:31 -0700197 }
198
Yi Tseng919b2df2017-09-07 16:22:51 -0700199 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
200 serverInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
201 hostService.stopMonitoringIp(gatewayIp);
202 });
203 serverInfo.getDhcpServerIp4().ifPresent(serverIp -> {
204 hostService.stopMonitoringIp(serverIp);
205 });
Yi Tseng51301292017-07-28 13:02:59 -0700206 }
207
208 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700209 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -0700210 setDhcpServerConfigs(configs, defaultServerInfoList);
211 }
212
213 @Override
214 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
215 setDhcpServerConfigs(configs, indirectServerInfoList);
216 }
217
218 @Override
219 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
220 return defaultServerInfoList;
221 }
222
223 @Override
224 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
225 return indirectServerInfoList;
226 }
227
Yi Tseng525ff402017-10-23 19:39:39 -0700228 @Override
229 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
230 if (config == null) {
231 ignoredVlans.forEach(((deviceId, vlanId) -> {
232 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
233 }));
234 return;
235 }
236 config.ignoredVlans().forEach((deviceId, vlanId) -> {
237 if (ignoredVlans.get(deviceId).contains(vlanId)) {
238 // don't need to process if it already ignored
239 return;
240 }
241 processIgnoreVlanRule(deviceId, vlanId, ADD);
242 });
243
244 ignoredVlans.forEach((deviceId, vlanId) -> {
245 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
246 // not contains in new config, remove it
247 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
248 }
249 });
250 }
251
Saurav Dasb805f1a2017-12-13 16:19:35 -0800252 @Override
253 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
254 if (config == null) {
255 ignoredVlans.clear();
256 return;
257 }
258 config.ignoredVlans().forEach((deviceId, vlanId) -> {
259 ignoredVlans.remove(deviceId, vlanId);
260 });
261 }
262
Yi Tseng919b2df2017-09-07 16:22:51 -0700263 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700264 if (configs.size() == 0) {
265 // no config to update
266 return;
267 }
268
rsahot036620655b2018-02-26 15:01:37 -0500269 Boolean isConfigValid = false;
270 for (DhcpServerConfig serverConfig : configs) {
271 if (serverConfig.getDhcpServerIp4().isPresent()) {
272 isConfigValid = true;
273 break;
274 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700275 }
rsahot036620655b2018-02-26 15:01:37 -0500276 if (!isConfigValid) {
277 log.warn("No IP V4 server address found.");
278 return; // No IP V6 address found
279 }
280 // if (!serverInfoList.isEmpty()) {
281 for (DhcpServerInfo oldServerInfo : serverInfoList) {
282 log.info("In for (DhcpServerInfo oldServerInfo : serverInfoList) {");
Yi Tseng919b2df2017-09-07 16:22:51 -0700283 // remove old server info
rsahot036620655b2018-02-26 15:01:37 -0500284 //DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700285
Yi Tseng919b2df2017-09-07 16:22:51 -0700286 // stop monitoring gateway or server
287 oldServerInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
288 hostService.stopMonitoringIp(gatewayIp);
289 });
290 oldServerInfo.getDhcpServerIp4().ifPresent(serverIp -> {
291 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -0700292 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -0700293 });
Yi Tseng483ac6f2017-08-02 15:03:31 -0700294 }
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700295
Yi Tseng919b2df2017-09-07 16:22:51 -0700296 // Create new server info according to the config
rsahot036620655b2018-02-26 15:01:37 -0500297 serverInfoList.clear();
298 for (DhcpServerConfig serverConfig : configs) {
299 log.info("// Create new server info according to the config");
300 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
301 DhcpServerInfo.Version.DHCP_V4);
302 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
303 "Connect point not exists");
304 checkState(newServerInfo.getDhcpServerIp4().isPresent(),
305 "IP of DHCP server not exists");
Yi Tseng4f2a0462017-08-31 11:21:00 -0700306
rsahot036620655b2018-02-26 15:01:37 -0500307 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
308 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -0700309
rsahot036620655b2018-02-26 15:01:37 -0500310 Ip4Address serverIp = newServerInfo.getDhcpServerIp4().get();
311 Ip4Address ipToProbe;
312 if (newServerInfo.getDhcpGatewayIp4().isPresent()) {
313 ipToProbe = newServerInfo.getDhcpGatewayIp4().get();
314 } else {
315 ipToProbe = newServerInfo.getDhcpServerIp4().orElse(null);
316 }
317 log.info("Probe_IP {}", ipToProbe);
318 String hostToProbe = newServerInfo.getDhcpGatewayIp4()
319 .map(ip -> "gateway").orElse("server");
320
321 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
322 hostService.startMonitoringIp(ipToProbe);
323
324 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
325 if (!hosts.isEmpty()) {
326 Host host = hosts.iterator().next();
327 newServerInfo.setDhcpConnectVlan(host.vlan());
328 newServerInfo.setDhcpConnectMac(host.mac());
329 }
330
331 // Add new server info
332 synchronized (this) {
333 //serverInfoList.clear();
334 serverInfoList.add(newServerInfo);
335 }
336
337 requestDhcpPacket(serverIp);
Yi Tseng4f2a0462017-08-31 11:21:00 -0700338 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700339 }
340
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700341 @Override
Yi Tseng51301292017-07-28 13:02:59 -0700342 public void processDhcpPacket(PacketContext context, BasePacket payload) {
343 checkNotNull(payload, "DHCP payload can't be null");
344 checkState(payload instanceof DHCP, "Payload is not a DHCP");
345 DHCP dhcpPayload = (DHCP) payload;
346 if (!configured()) {
Yi Tseng919b2df2017-09-07 16:22:51 -0700347 log.warn("Missing default DHCP relay server config. Abort packet processing");
Yi Tseng51301292017-07-28 13:02:59 -0700348 return;
349 }
350
351 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tseng51301292017-07-28 13:02:59 -0700352 checkNotNull(dhcpPayload, "Can't find DHCP payload");
353 Ethernet packet = context.inPacket().parsed();
354 DHCP.MsgType incomingPacketType = dhcpPayload.getOptions().stream()
355 .filter(dhcpOption -> dhcpOption.getCode() == OptionCode_MessageType.getValue())
356 .map(DhcpOption::getData)
357 .map(data -> DHCP.MsgType.getType(data[0]))
358 .findFirst()
359 .orElse(null);
360 checkNotNull(incomingPacketType, "Can't get message type from DHCP payload {}", dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500361 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
362 //ignore the packets if dhcp client interface is not configured on onos.
363 if (receivingInterfaces.isEmpty()) {
364 log.warn("Virtual interface is not configured on {}", inPort);
365 return;
366 }
Yi Tseng51301292017-07-28 13:02:59 -0700367 switch (incomingPacketType) {
368 case DHCPDISCOVER:
Yi Tsengdcef2c22017-08-05 20:34:06 -0700369 // Add the gateway IP as virtual interface IP for server to understand
Yi Tseng51301292017-07-28 13:02:59 -0700370 // the lease to be assigned and forward the packet to dhcp server.
rsahot036620655b2018-02-26 15:01:37 -0500371 List<InternalPacket> ethernetClientPacket =
372 processDhcpPacketFromClient(context, packet, receivingInterfaces);
373 for (InternalPacket internalPacket : ethernetClientPacket) {
374 log.debug("DHCPDISCOVER from {} Forward to server", inPort);
Yi Tseng51301292017-07-28 13:02:59 -0700375 writeRequestDhcpRecord(inPort, packet, dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500376 forwardPacket(internalPacket);
Yi Tseng51301292017-07-28 13:02:59 -0700377 }
378 break;
379 case DHCPOFFER:
380 //reply to dhcp client.
rsahot036620655b2018-02-26 15:01:37 -0500381 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(context, packet);
Yi Tseng51301292017-07-28 13:02:59 -0700382 if (ethernetPacketOffer != null) {
383 writeResponseDhcpRecord(ethernetPacketOffer, dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700384 sendResponseToClient(ethernetPacketOffer, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -0700385 }
386 break;
387 case DHCPREQUEST:
388 // add the gateway ip as virtual interface ip for server to understand
389 // the lease to be assigned and forward the packet to dhcp server.
rsahot036620655b2018-02-26 15:01:37 -0500390 List<InternalPacket> ethernetPacketRequest =
391 processDhcpPacketFromClient(context, packet, receivingInterfaces);
392 for (InternalPacket internalPacket : ethernetPacketRequest) {
393 log.debug("DHCPDISCOVER from {} Forward to server", inPort);
Yi Tseng51301292017-07-28 13:02:59 -0700394 writeRequestDhcpRecord(inPort, packet, dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500395 forwardPacket(internalPacket);
Yi Tseng51301292017-07-28 13:02:59 -0700396 }
397 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400398 case DHCPDECLINE:
399 break;
Yi Tseng51301292017-07-28 13:02:59 -0700400 case DHCPACK:
401 // reply to dhcp client.
rsahot036620655b2018-02-26 15:01:37 -0500402 Ethernet ethernetPacketAck = processDhcpPacketFromServer(context, packet);
Yi Tseng51301292017-07-28 13:02:59 -0700403 if (ethernetPacketAck != null) {
404 writeResponseDhcpRecord(ethernetPacketAck, dhcpPayload);
405 handleDhcpAck(ethernetPacketAck, dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700406 sendResponseToClient(ethernetPacketAck, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -0700407 }
408 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400409 case DHCPNAK:
410 break;
Yi Tseng51301292017-07-28 13:02:59 -0700411 case DHCPRELEASE:
412 // TODO: release the ip address from client
413 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400414 case DHCPINFORM:
415 break;
416 case DHCPFORCERENEW:
417 break;
418 case DHCPLEASEQUERY:
419 handleLeaseQueryMsg(context, packet, dhcpPayload);
420 break;
421 case DHCPLEASEACTIVE:
422 handleLeaseQueryActivateMsg(packet, dhcpPayload);
423 break;
424 case DHCPLEASEUNASSIGNED:
425 case DHCPLEASEUNKNOWN:
426 handleLeaseQueryUnknown(packet, dhcpPayload);
427 break;
Yi Tseng51301292017-07-28 13:02:59 -0700428 default:
429 break;
430 }
431 }
432
433 /**
434 * Checks if this app has been configured.
435 *
436 * @return true if all information we need have been initialized
437 */
Yi Tseng4f2a0462017-08-31 11:21:00 -0700438 private boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700439 return !defaultServerInfoList.isEmpty();
Yi Tseng51301292017-07-28 13:02:59 -0700440 }
441
442 /**
Yi Tsengdcef2c22017-08-05 20:34:06 -0700443 * Returns the first interface ip from interface.
Yi Tseng51301292017-07-28 13:02:59 -0700444 *
Yi Tsengdcef2c22017-08-05 20:34:06 -0700445 * @param iface interface of one connect point
Yi Tseng51301292017-07-28 13:02:59 -0700446 * @return the first interface IP; null if not exists an IP address in
447 * these interfaces
448 */
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700449 private Ip4Address getFirstIpFromInterface(Interface iface) {
Yi Tsengdcef2c22017-08-05 20:34:06 -0700450 checkNotNull(iface, "Interface can't be null");
451 return iface.ipAddressesList().stream()
Yi Tseng51301292017-07-28 13:02:59 -0700452 .map(InterfaceIpAddress::ipAddress)
453 .filter(IpAddress::isIp4)
454 .map(IpAddress::getIp4Address)
455 .findFirst()
456 .orElse(null);
457 }
458
459 /**
Yi Tseng4f2a0462017-08-31 11:21:00 -0700460 * Gets Interface facing to the server for default host.
Yi Tsengdcef2c22017-08-05 20:34:06 -0700461 *
462 * @return the Interface facing to the server; null if not found
463 */
Yi Tseng919b2df2017-09-07 16:22:51 -0700464 private Interface getDefaultServerInterface() {
465 return getServerInterface(defaultServerInfoList);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700466 }
467
468 /**
Yi Tseng4f2a0462017-08-31 11:21:00 -0700469 * Gets Interface facing to the server for indirect hosts.
470 * Use default server Interface if indirect server not configured.
471 *
472 * @return the Interface facing to the server; null if not found
473 */
474 private Interface getIndirectServerInterface() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700475 return getServerInterface(indirectServerInfoList);
476 }
477
478 private Interface getServerInterface(List<DhcpServerInfo> serverInfos) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800479 return serverInfos.stream()
Yi Tseng4f2a0462017-08-31 11:21:00 -0700480 .findFirst()
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800481 .map(serverInfo -> {
482 ConnectPoint dhcpServerConnectPoint =
483 serverInfo.getDhcpServerConnectPoint().orElse(null);
484 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
485 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
486 return null;
487 }
488 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
489 .stream()
490 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
491 .findFirst()
492 .orElse(null);
493 })
Yi Tseng4f2a0462017-08-31 11:21:00 -0700494 .orElse(null);
495 }
496
497 /**
498 * Determind if an Interface contains a vlan id.
499 *
500 * @param iface the Interface
501 * @param vlanId the vlan id
502 * @return true if the Interface contains the vlan id
503 */
504 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
Yi Tseng4025a102017-09-30 11:35:42 +0800505 if (vlanId.equals(VlanId.NONE)) {
506 // untagged packet, check if vlan untagged or vlan native is not NONE
507 return !iface.vlanUntagged().equals(VlanId.NONE) ||
508 !iface.vlanNative().equals(VlanId.NONE);
509 }
510 // tagged packet, check if the interface contains the vlan
511 return iface.vlanTagged().contains(vlanId);
Yi Tseng4f2a0462017-08-31 11:21:00 -0700512 }
513
Charles Chand0dd7002017-10-08 23:53:36 -0400514 private void handleLeaseQueryActivateMsg(Ethernet packet, DHCP dhcpPayload) {
515 log.debug("LQ: Got DHCPLEASEACTIVE packet!");
516
517 // TODO: release the ip address from client
518 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
519 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
520 HostId hostId = HostId.hostId(clientMacAddress, vlanId);
521 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
522
523 if (record == null) {
524 log.warn("Can't find record for host {} when processing DHCPLEASEACTIVE", hostId);
525 return;
526 }
527
528 // need to update routes
529 log.debug("Lease Query for Client results in DHCPLEASEACTIVE - route needs to be modified");
530 // get current route
531 // find the ip of that client with the DhcpRelay store
532
533 Ip4Address clientIP = record.ip4Address().orElse(null);
534 log.debug("LQ: IP of host is " + clientIP.getIp4Address());
535
536 MacAddress nextHopMac = record.nextHop().orElse(null);
537 log.debug("LQ: MAC of resulting *OLD* NH for that host is " + nextHopMac.toString());
538
539 // find the new NH by looking at the src MAC of the dhcp request
540 // from the LQ store
541 MacAddress newNextHopMac = record.nextHopTemp().orElse(null);
542 log.debug("LQ: MAC of resulting *NEW* NH for that host is " + newNextHopMac.toString());
543
544 log.debug("LQ: updating dhcp relay record with new NH");
545 record.nextHop(newNextHopMac);
546
547 // find the next hop IP from its mac
548 HostId gwHostId = HostId.hostId(newNextHopMac, vlanId);
549 Host gwHost = hostService.getHost(gwHostId);
550
551 if (gwHost == null) {
552 log.warn("Can't find gateway for new NH host " + gwHostId);
553 return;
554 }
555
556 Ip4Address nextHopIp = gwHost.ipAddresses()
557 .stream()
558 .filter(IpAddress::isIp4)
559 .map(IpAddress::getIp4Address)
560 .findFirst()
561 .orElse(null);
562
563 if (nextHopIp == null) {
564 log.warn("Can't find IP address of gateway " + gwHost);
565 return;
566 }
567
568 log.debug("LQ: *NEW* NH IP for host is " + nextHopIp.getIp4Address());
Charles Chan6305b692018-04-04 11:43:54 -0700569 Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
Charles Chand0dd7002017-10-08 23:53:36 -0400570 routeStore.updateRoute(route);
571
572 // and forward to client
573 Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
574 if (ethernetPacket != null) {
575 sendResponseToClient(ethernetPacket, dhcpPayload);
576 }
577 }
578
579 /**
580 *
581 */
582 private void handleLeaseQueryMsg(PacketContext context, Ethernet packet, DHCP dhcpPayload) {
583 log.debug("LQ: Got DHCPLEASEQUERY packet!");
584 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
585 log.debug("LQ: got DHCPLEASEQUERY with MAC " + clientMacAddress.toString());
586 // add the client mac (hostid) of this request to a store (the entry will be removed with
587 // the reply sent to the originator)
588 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
589 HostId hId = HostId.hostId(clientMacAddress, vlanId);
590 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hId).orElse(null);
591 if (record != null) {
592 //new NH is to be taken from src mac of LQ packet
593 MacAddress newNextHop = packet.getSourceMAC();
594 record.nextHopTemp(newNextHop);
595 record.ip4Status(dhcpPayload.getPacketType());
596 record.updateLastSeen();
597
598 // do a basic routing of the packet (this is unicast routing
599 // not a relay operation like for other broadcast dhcp packets
rsahot036620655b2018-02-26 15:01:37 -0500600 List<InternalPacket> ethernetPacketRequest = processLeaseQueryFromAgent(context, packet);
Charles Chand0dd7002017-10-08 23:53:36 -0400601 // and forward to server
rsahot036620655b2018-02-26 15:01:37 -0500602 for (InternalPacket internalPacket : ethernetPacketRequest) {
603 log.debug("LeaseQueryMsg forward to server");
604 forwardPacket(internalPacket);
605 }
Charles Chand0dd7002017-10-08 23:53:36 -0400606 } else {
607 log.warn("LQ: Error! - DHCP relay record for that client not found - ignoring LQ!");
608 }
609 }
610
611 private void handleLeaseQueryUnknown(Ethernet packet, DHCP dhcpPayload) {
612 log.debug("Lease Query for Client results in DHCPLEASEUNASSIGNED or " +
613 "DHCPLEASEUNKNOWN - removing route & forwarding reply to originator");
614 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
615 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
616 HostId hostId = HostId.hostId(clientMacAddress, vlanId);
617 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
618
619 if (record == null) {
620 log.warn("Can't find record for host {} when handling LQ UNKNOWN/UNASSIGNED message", hostId);
621 return;
622 }
623
624 Ip4Address clientIP = record.ip4Address().orElse(null);
625 log.debug("LQ: IP of host is " + clientIP.getIp4Address());
626
627 // find the new NH by looking at the src MAC of the dhcp request
628 // from the LQ store
629 MacAddress nextHopMac = record.nextHop().orElse(null);
630 log.debug("LQ: MAC of resulting *Existing* NH for that route is " + nextHopMac.toString());
631
632 // find the next hop IP from its mac
633 HostId gwHostId = HostId.hostId(nextHopMac, vlanId);
634 Host gwHost = hostService.getHost(gwHostId);
635
636 if (gwHost == null) {
637 log.warn("Can't find gateway for new NH host " + gwHostId);
638 return;
639 }
640
641 Ip4Address nextHopIp = gwHost.ipAddresses()
642 .stream()
643 .filter(IpAddress::isIp4)
644 .map(IpAddress::getIp4Address)
645 .findFirst()
646 .orElse(null);
647
648 if (nextHopIp == null) {
649 log.warn("Can't find IP address of gateway {}", gwHost);
650 return;
651 }
652
653 log.debug("LQ: *Existing* NH IP for host is " + nextHopIp.getIp4Address() + " removing route for it");
Charles Chan6305b692018-04-04 11:43:54 -0700654 Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
Charles Chand0dd7002017-10-08 23:53:36 -0400655 routeStore.removeRoute(route);
656
657 // remove from temp store
658 dhcpRelayStore.removeDhcpRecord(hostId);
659
660 // and forward to client
661 Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
662 if (ethernetPacket != null) {
663 sendResponseToClient(ethernetPacket, dhcpPayload);
664 }
665 }
666
Yi Tseng4f2a0462017-08-31 11:21:00 -0700667 /**
Yi Tseng51301292017-07-28 13:02:59 -0700668 * Build the DHCP discover/request packet with gateway IP(unicast packet).
669 *
670 * @param context the packet context
671 * @param ethernetPacket the ethernet payload to process
Yi Tseng51301292017-07-28 13:02:59 -0700672 * @return processed packet
673 */
rsahot036620655b2018-02-26 15:01:37 -0500674 private List<InternalPacket> processDhcpPacketFromClient(PacketContext context,
675 Ethernet ethernetPacket,
676 Set<Interface> clientInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -0700677 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
678 DeviceId receivedFromDevice = receivedFrom.deviceId();
rsahot036620655b2018-02-26 15:01:37 -0500679 Ip4Address relayAgentIp = null;
680 relayAgentIp = dhcp4HandlerUtil.getRelayAgentIPv4Address(clientInterfaces);
681 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
682 if (relayAgentIp == null || relayAgentMac == null) {
683 log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
684 + "packet from client on port: {}. Aborting packet processing",
685 clientInterfaces.iterator().next().connectPoint());
Charles Chane5e0c9a2018-03-30 12:11:34 -0700686 return Lists.newArrayList();
rsahot036620655b2018-02-26 15:01:37 -0500687 }
688 log.debug("Multi DHCP V4 processDhcpPacketFromClient on port {}",
689 clientInterfaces.iterator().next().connectPoint());
Yi Tseng25bfe372017-11-03 16:27:32 -0700690
Yi Tseng4f2a0462017-08-31 11:21:00 -0700691 // get dhcp header.
rsahot036620655b2018-02-26 15:01:37 -0500692 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Yi Tseng4f2a0462017-08-31 11:21:00 -0700693 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
694 UDP udpPacket = (UDP) ipv4Packet.getPayload();
695 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
696
Yi Tseng919b2df2017-09-07 16:22:51 -0700697
Yi Tsengdcef2c22017-08-05 20:34:06 -0700698 Ip4Address clientInterfaceIp =
699 interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
700 .stream()
701 .map(Interface::ipAddressesList)
702 .flatMap(Collection::stream)
703 .map(InterfaceIpAddress::ipAddress)
704 .filter(IpAddress::isIp4)
705 .map(IpAddress::getIp4Address)
706 .findFirst()
707 .orElse(null);
708 if (clientInterfaceIp == null) {
709 log.warn("Can't find interface IP for client interface for port {}",
rsahot036620655b2018-02-26 15:01:37 -0500710 context.inPacket().receivedFrom());
Charles Chane5e0c9a2018-03-30 12:11:34 -0700711 return Lists.newArrayList();
Yi Tsengdcef2c22017-08-05 20:34:06 -0700712 }
rsahot036620655b2018-02-26 15:01:37 -0500713
Yi Tseng4f2a0462017-08-31 11:21:00 -0700714 boolean isDirectlyConnected = directlyConnected(dhcpPacket);
rsahot036620655b2018-02-26 15:01:37 -0500715 boolean directConnFlag = directlyConnected(dhcpPacket);
716
717 // Multi DHCP Start
718 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
719 VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
720 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
721 .stream().filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
722 .findFirst()
723 .orElse(null);
724
725 List<InternalPacket> internalPackets = new ArrayList<>();
726 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
727 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
728
729
730 for (DhcpServerInfo serverInfo : copyServerInfoList) {
731 etherReply = (Ethernet) ethernetPacket.clone();
732 ipv4Packet = (IPv4) etherReply.getPayload();
733 udpPacket = (UDP) ipv4Packet.getPayload();
734 dhcpPacket = (DHCP) udpPacket.getPayload();
735 if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
736 log.warn("Can't get server connect point, ignore");
737 continue;
738 }
739 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
740 if (newServerInfo == null) {
741 log.warn("Can't get server interface with host info resolved, ignore");
742 continue;
743 }
744
745 Interface serverInterface = getServerInterface(newServerInfo);
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800746 if (serverInterface == null) {
rsahot036620655b2018-02-26 15:01:37 -0500747 log.warn("Can't get server interface, ignore");
748 continue;
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800749 }
Yi Tseng4f2a0462017-08-31 11:21:00 -0700750
rsahot036620655b2018-02-26 15:01:37 -0500751 Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
752 MacAddress macFacingServer = serverInterface.mac();
753 log.debug("Interfacing server {} Mac : {} ", ipFacingServer, macFacingServer);
754 if (ipFacingServer == null || macFacingServer == null) {
755 log.warn("No IP address for server Interface {}", serverInterface);
rsahot036620655b2018-02-26 15:01:37 -0500756 continue;
757 }
Yi Tseng51301292017-07-28 13:02:59 -0700758
Charles Chand0d1e332017-10-10 16:53:32 -0400759
rsahot036620655b2018-02-26 15:01:37 -0500760 etherReply.setSourceMACAddress(macFacingServer);
761 // set default info and replace with indirect if available later on.
762 if (newServerInfo.getDhcpConnectMac().isPresent()) {
763 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
764 }
765 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
766 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
767 }
768 ipv4Packet.setSourceAddress(ipFacingServer.toInt());
769 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
Charles Chan2de55302018-03-02 18:27:59 -0800770 log.debug("Directly connected {}", isDirectlyConnected);
771 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().get());
rsahot036620655b2018-02-26 15:01:37 -0500772 if (isDirectlyConnected) {
Yi Tseng51301292017-07-28 13:02:59 -0700773
Charles Chan2de55302018-03-02 18:27:59 -0800774 log.debug("Default DHCP server IP: {}", newServerInfo.getDhcpServerIp4().get());
rsahot036620655b2018-02-26 15:01:37 -0500775 if (newServerInfo.getDhcpConnectMac().isPresent()) {
776 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
Charles Chand0d1e332017-10-10 16:53:32 -0400777 }
rsahot036620655b2018-02-26 15:01:37 -0500778 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
779 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
780 }
781
782 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
783
784
785 ConnectPoint inPort = context.inPacket().receivedFrom();
786 VlanId vlanId = VlanId.vlanId(ethernetPacket.getVlanID());
787 // add connected in port and vlan
788 CircuitId cid = new CircuitId(inPort.toString(), vlanId);
789 byte[] circuitId = cid.serialize();
790 DhcpOption circuitIdSubOpt = new DhcpOption();
791 circuitIdSubOpt
792 .setCode(CIRCUIT_ID.getValue())
793 .setLength((byte) circuitId.length)
794 .setData(circuitId);
795
796 DhcpRelayAgentOption newRelayAgentOpt = new DhcpRelayAgentOption();
797 newRelayAgentOpt.setCode(OptionCode_CircuitID.getValue());
798 newRelayAgentOpt.addSubOption(circuitIdSubOpt);
799
800 // Removes END option first
801 List<DhcpOption> options = dhcpPacket.getOptions().stream()
802 .filter(opt -> opt.getCode() != OptionCode_END.getValue())
803 .collect(Collectors.toList());
804
805 // push relay agent option
806 options.add(newRelayAgentOpt);
807
808 // make sure option 255(End) is the last option
809 DhcpOption endOption = new DhcpOption();
810 endOption.setCode(OptionCode_END.getValue());
811 options.add(endOption);
812
813 dhcpPacket.setOptions(options);
814
815 relayAgentIp = serverInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
816
817 // Sets relay agent IP
818 int effectiveRelayAgentIp = relayAgentIp != null ?
819 relayAgentIp.toInt() : clientInterfaceIp.toInt();
820 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
Charles Chan2de55302018-03-02 18:27:59 -0800821 log.debug("In Default, Relay Agent IP {}", effectiveRelayAgentIp);
Charles Chand0d1e332017-10-10 16:53:32 -0400822 } else {
rsahot036620655b2018-02-26 15:01:37 -0500823 if (!newServerInfo.getDhcpServerIp4().isPresent()) {
824 // do nothing
825 } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
826 continue;
827 } else {
828 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
829 // Sets relay agent IP
830 int effectiveRelayAgentIp = relayAgentIp != null ?
831 relayAgentIp.toInt() : clientInterfaceIp.toInt();
832 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
Charles Chand0d1e332017-10-10 16:53:32 -0400833 }
Yi Tseng4f2a0462017-08-31 11:21:00 -0700834 }
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700835
rsahot036620655b2018-02-26 15:01:37 -0500836 // Remove broadcast flag
837 dhcpPacket.setFlags((short) 0);
838
839 udpPacket.setPayload(dhcpPacket);
840 // As a DHCP relay, the source port should be server port( instead
841 // of client port.
842 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
843 udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
844 ipv4Packet.setPayload(udpPacket);
845 ipv4Packet.setTtl((byte) 64);
846 etherReply.setPayload(ipv4Packet);
847 InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
848 serverInfo.getDhcpServerConnectPoint().get());
849 internalPackets.add(internalPacket);
850 }
851 return internalPackets;
Yi Tseng51301292017-07-28 13:02:59 -0700852 }
853
Charles Chand0dd7002017-10-08 23:53:36 -0400854
855 /**
856 * Do a basic routing for a packet from client (used for LQ processing).
857 *
858 * @param context the packet context
859 * @param ethernetPacket the ethernet payload to process
860 * @return processed packet
861 */
rsahot036620655b2018-02-26 15:01:37 -0500862 private List<InternalPacket> processLeaseQueryFromAgent(PacketContext context,
863 Ethernet ethernetPacket) {
864 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
865 DeviceId receivedFromDevice = receivedFrom.deviceId();
866
Charles Chand0dd7002017-10-08 23:53:36 -0400867 // get dhcp header.
868 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
869 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
870 UDP udpPacket = (UDP) ipv4Packet.getPayload();
871 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
872
Charles Chan2de55302018-03-02 18:27:59 -0800873 Ip4Address relayAgentIp;
Charles Chand0dd7002017-10-08 23:53:36 -0400874
Charles Chand0dd7002017-10-08 23:53:36 -0400875 Ip4Address clientInterfaceIp =
876 interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
877 .stream()
878 .map(Interface::ipAddressesList)
879 .flatMap(Collection::stream)
880 .map(InterfaceIpAddress::ipAddress)
881 .filter(IpAddress::isIp4)
882 .map(IpAddress::getIp4Address)
883 .findFirst()
884 .orElse(null);
885 if (clientInterfaceIp == null) {
886 log.warn("Can't find interface IP for client interface for port {}",
rsahot036620655b2018-02-26 15:01:37 -0500887 context.inPacket().receivedFrom());
Charles Chand0dd7002017-10-08 23:53:36 -0400888 return null;
889 }
rsahot036620655b2018-02-26 15:01:37 -0500890
Charles Chand0dd7002017-10-08 23:53:36 -0400891 boolean isDirectlyConnected = directlyConnected(dhcpPacket);
rsahot036620655b2018-02-26 15:01:37 -0500892 boolean directConnFlag = directlyConnected(dhcpPacket);
893
894 // Multi DHCP Start
895 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
896 VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
rsahot036620655b2018-02-26 15:01:37 -0500897
898 List<InternalPacket> internalPackets = new ArrayList<>();
899 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
Charles Chan2de55302018-03-02 18:27:59 -0800900 List<DhcpServerInfo> copyServerInfoList = new ArrayList<>(serverInfoList);
rsahot036620655b2018-02-26 15:01:37 -0500901
902 for (DhcpServerInfo serverInfo : copyServerInfoList) {
903 // get dhcp header.
904 etherReply = (Ethernet) ethernetPacket.clone();
905 ipv4Packet = (IPv4) etherReply.getPayload();
906 udpPacket = (UDP) ipv4Packet.getPayload();
907 dhcpPacket = (DHCP) udpPacket.getPayload();
908
909 if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
910 log.warn("Can't get server connect point, ignore");
911 continue;
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800912 }
rsahot036620655b2018-02-26 15:01:37 -0500913 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
914 if (newServerInfo == null) {
915 log.warn("Can't get server interface with host info resolved, ignore");
916 continue;
917 }
Charles Chand0dd7002017-10-08 23:53:36 -0400918
rsahot036620655b2018-02-26 15:01:37 -0500919 Interface serverInterface = getServerInterface(newServerInfo);
920 if (serverInterface == null) {
921 log.warn("Can't get server interface, ignore");
922 continue;
923 }
924 Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
925 MacAddress macFacingServer = serverInterface.mac();
926 if (ipFacingServer == null || macFacingServer == null) {
927 log.warn("No IP address for server Interface {}", serverInterface);
928 continue;
929 }
Charles Chand0dd7002017-10-08 23:53:36 -0400930
rsahot036620655b2018-02-26 15:01:37 -0500931 etherReply.setSourceMACAddress(macFacingServer);
Charles Chan2de55302018-03-02 18:27:59 -0800932 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
933 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
rsahot036620655b2018-02-26 15:01:37 -0500934 ipv4Packet.setSourceAddress(ipFacingServer.toInt());
Charles Chan2de55302018-03-02 18:27:59 -0800935 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
rsahot036620655b2018-02-26 15:01:37 -0500936 if (isDirectlyConnected) {
937 // set default info and replace with indirect if available later on.
938 if (newServerInfo.getDhcpConnectMac().isPresent()) {
939 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
940 }
941 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
942 etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
943 }
944 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
945 // Sets relay agent IP
946 int effectiveRelayAgentIp = relayAgentIp != null ?
947 relayAgentIp.toInt() : clientInterfaceIp.toInt();
948 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
949 } else {
950 if (!newServerInfo.getDhcpServerIp4().isPresent()) {
951 //do nothing
952 } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
953 continue;
954 } else {
955 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
956 // Sets relay agent IP
957 int effectiveRelayAgentIp = relayAgentIp != null ?
958 relayAgentIp.toInt() : clientInterfaceIp.toInt();
959 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
960 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
961 log.info("Relay Agent IP {}", relayAgentIp);
962 }
963
964 log.info("In Direct");
965 }
966
967 // Remove broadcast flag
968 dhcpPacket.setFlags((short) 0);
969
970 udpPacket.setPayload(dhcpPacket);
971 // As a DHCP relay, the source port should be server port( instead
972 // of client port.
973 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
974 udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
975 ipv4Packet.setPayload(udpPacket);
976 ipv4Packet.setTtl((byte) 64);
977 etherReply.setPayload(ipv4Packet);
978 ////return etherReply;
979 Dhcp4HandlerUtil.InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
980 newServerInfo.getDhcpServerConnectPoint().get());
981 internalPackets.add(internalPacket);
Charles Chand0dd7002017-10-08 23:53:36 -0400982 }
rsahot036620655b2018-02-26 15:01:37 -0500983 log.warn("num of processLeaseQueryFromAgent packets to send is{}", internalPackets.size());
Charles Chand0dd7002017-10-08 23:53:36 -0400984
rsahot036620655b2018-02-26 15:01:37 -0500985 return internalPackets;
Charles Chand0dd7002017-10-08 23:53:36 -0400986 }
987
988
Yi Tseng51301292017-07-28 13:02:59 -0700989 /**
990 * Writes DHCP record to the store according to the request DHCP packet (Discover, Request).
991 *
992 * @param location the location which DHCP packet comes from
993 * @param ethernet the DHCP packet
994 * @param dhcpPayload the DHCP payload
995 */
996 private void writeRequestDhcpRecord(ConnectPoint location,
997 Ethernet ethernet,
998 DHCP dhcpPayload) {
999 VlanId vlanId = VlanId.vlanId(ethernet.getVlanID());
1000 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1001 HostId hostId = HostId.hostId(macAddress, vlanId);
1002 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1003 if (record == null) {
1004 record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
1005 } else {
1006 record = record.clone();
1007 }
1008 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
1009 record.ip4Status(dhcpPayload.getPacketType());
1010 record.setDirectlyConnected(directlyConnected(dhcpPayload));
1011 if (!directlyConnected(dhcpPayload)) {
1012 // Update gateway mac address if the host is not directly connected
1013 record.nextHop(ethernet.getSourceMAC());
1014 }
1015 record.updateLastSeen();
1016 dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
1017 }
1018
1019 /**
1020 * Writes DHCP record to the store according to the response DHCP packet (Offer, Ack).
1021 *
1022 * @param ethernet the DHCP packet
1023 * @param dhcpPayload the DHCP payload
1024 */
1025 private void writeResponseDhcpRecord(Ethernet ethernet,
1026 DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001027 Optional<Interface> outInterface = getClientInterface(ethernet, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -07001028 if (!outInterface.isPresent()) {
1029 log.warn("Failed to determine where to send {}", dhcpPayload.getPacketType());
1030 return;
1031 }
1032
1033 Interface outIface = outInterface.get();
1034 ConnectPoint location = outIface.connectPoint();
Yi Tseng4f2a0462017-08-31 11:21:00 -07001035 VlanId vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001036 if (vlanId == null) {
1037 vlanId = outIface.vlan();
1038 }
Yi Tseng51301292017-07-28 13:02:59 -07001039 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1040 HostId hostId = HostId.hostId(macAddress, vlanId);
1041 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1042 if (record == null) {
1043 record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
1044 } else {
1045 record = record.clone();
1046 }
1047 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
1048 if (dhcpPayload.getPacketType() == DHCP.MsgType.DHCPACK) {
1049 record.ip4Address(Ip4Address.valueOf(dhcpPayload.getYourIPAddress()));
1050 }
1051 record.ip4Status(dhcpPayload.getPacketType());
1052 record.setDirectlyConnected(directlyConnected(dhcpPayload));
1053 record.updateLastSeen();
1054 dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
1055 }
1056
1057 /**
1058 * Build the DHCP offer/ack with proper client port.
1059 *
1060 * @param ethernetPacket the original packet comes from server
1061 * @return new packet which will send to the client
1062 */
rsahot036620655b2018-02-26 15:01:37 -05001063 private Ethernet processDhcpPacketFromServer(PacketContext context, Ethernet ethernetPacket) {
Yi Tseng51301292017-07-28 13:02:59 -07001064 // get dhcp header.
rsahot036620655b2018-02-26 15:01:37 -05001065 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Yi Tseng51301292017-07-28 13:02:59 -07001066 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
1067 UDP udpPacket = (UDP) ipv4Packet.getPayload();
1068 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
1069
1070 // determine the vlanId of the client host - note that this vlan id
1071 // could be different from the vlan in the packet from the server
Yi Tsengdcef2c22017-08-05 20:34:06 -07001072 Interface clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
Yi Tseng51301292017-07-28 13:02:59 -07001073
Yi Tsengdcef2c22017-08-05 20:34:06 -07001074 if (clientInterface == null) {
Yi Tseng51301292017-07-28 13:02:59 -07001075 log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
1076 return null;
1077 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001078 VlanId vlanId;
rsahot036620655b2018-02-26 15:01:37 -05001079 ConnectPoint inPort = context.inPacket().receivedFrom();
1080 boolean directConnFlag = directlyConnected(dhcpPayload);
1081 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
1082
1083 if (foundServerInfo == null) {
1084 log.warn("Cannot find server info");
1085 return null;
1086 } else {
1087 if (dhcp4HandlerUtil.isServerIpEmpty(foundServerInfo)) {
1088 log.warn("Cannot find server info's ipaddress");
1089 return null;
1090 }
1091 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001092 if (clientInterface.vlanTagged().isEmpty()) {
1093 vlanId = clientInterface.vlan();
1094 } else {
1095 // might be multiple vlan in same interface
Yi Tseng4f2a0462017-08-31 11:21:00 -07001096 vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001097 }
1098 if (vlanId == null) {
1099 vlanId = VlanId.NONE;
1100 }
1101 etherReply.setVlanID(vlanId.toShort());
1102 etherReply.setSourceMACAddress(clientInterface.mac());
Yi Tseng51301292017-07-28 13:02:59 -07001103
Yi Tsengdcef2c22017-08-05 20:34:06 -07001104 if (!directlyConnected(dhcpPayload)) {
1105 // if client is indirectly connected, try use next hop mac address
1106 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1107 HostId hostId = HostId.hostId(macAddress, vlanId);
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001108 if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
1109 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1110 if (record != null) {
1111 // if next hop can be found, use mac address of next hop
1112 record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
1113 } else {
1114 // otherwise, discard the packet
1115 log.warn("Can't find record for host id {}, discard packet", hostId);
1116 return null;
1117 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001118 } else {
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001119 etherReply.setDestinationMACAddress(MacAddress.BROADCAST);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001120 }
Yi Tseng1696f562017-08-17 17:43:38 -07001121 } else {
1122 etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
Yi Tsengdcef2c22017-08-05 20:34:06 -07001123 }
1124
Yi Tseng51301292017-07-28 13:02:59 -07001125 // we leave the srcMac from the original packet
Yi Tseng51301292017-07-28 13:02:59 -07001126 // figure out the relay agent IP corresponding to the original request
Yi Tseng3df7f9d2017-08-17 13:08:31 -07001127 Ip4Address ipFacingClient = getFirstIpFromInterface(clientInterface);
1128 if (ipFacingClient == null) {
Yi Tseng51301292017-07-28 13:02:59 -07001129 log.warn("Cannot determine relay agent interface Ipv4 addr for host {}/{}. "
1130 + "Aborting relay for dhcp packet from server {}",
Yi Tsengdcef2c22017-08-05 20:34:06 -07001131 etherReply.getDestinationMAC(), clientInterface.vlan(),
Yi Tseng51301292017-07-28 13:02:59 -07001132 ethernetPacket);
1133 return null;
1134 }
1135 // SRC_IP: relay agent IP
1136 // DST_IP: offered IP
Yi Tseng3df7f9d2017-08-17 13:08:31 -07001137 ipv4Packet.setSourceAddress(ipFacingClient.toInt());
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001138 if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
1139 ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
1140 } else {
1141 ipv4Packet.setDestinationAddress(BROADCAST_IP);
1142 }
Yi Tseng51301292017-07-28 13:02:59 -07001143 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
1144 if (directlyConnected(dhcpPayload)) {
1145 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
1146 } else {
1147 // forward to another dhcp relay
Yi Tseng93ba53c2017-09-14 13:24:21 -07001148 // FIXME: Currently we assume the DHCP comes from a L2 relay with
1149 // Option 82, this might not work if DHCP message comes from
1150 // L3 relay.
1151 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
Yi Tseng51301292017-07-28 13:02:59 -07001152 }
1153
1154 udpPacket.setPayload(dhcpPayload);
1155 ipv4Packet.setPayload(udpPacket);
1156 etherReply.setPayload(ipv4Packet);
1157 return etherReply;
1158 }
1159
Yi Tsengdcef2c22017-08-05 20:34:06 -07001160 /**
Charles Chand0dd7002017-10-08 23:53:36 -04001161 * Build the DHCP offer/ack with proper client port.
1162 *
1163 * @param ethernetPacket the original packet comes from server
1164 * @return new packet which will send to the client
1165 */
1166 private Ethernet processLeaseQueryFromServer(Ethernet ethernetPacket) {
1167 // get dhcp header.
1168 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
1169 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
1170 UDP udpPacket = (UDP) ipv4Packet.getPayload();
1171 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
1172
1173 // determine the vlanId of the client host - note that this vlan id
1174 // could be different from the vlan in the packet from the server
1175 Interface clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
1176
1177 if (clientInterface == null) {
1178 log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
1179 return null;
1180 }
1181 VlanId vlanId;
1182 if (clientInterface.vlanTagged().isEmpty()) {
1183 vlanId = clientInterface.vlan();
1184 } else {
1185 // might be multiple vlan in same interface
1186 vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
1187 }
1188 if (vlanId == null) {
1189 vlanId = VlanId.NONE;
1190 }
1191 etherReply.setVlanID(vlanId.toShort());
1192 etherReply.setSourceMACAddress(clientInterface.mac());
1193
1194 if (!directlyConnected(dhcpPayload)) {
1195 // if client is indirectly connected, try use next hop mac address
1196 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1197 HostId hostId = HostId.hostId(macAddress, vlanId);
1198 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1199 if (record != null) {
1200 // if next hop can be found, use mac address of next hop
1201 record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
1202 } else {
1203 // otherwise, discard the packet
1204 log.warn("Can't find record for host id {}, discard packet", hostId);
1205 return null;
1206 }
1207 } else {
1208 etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
1209 }
1210
1211 // default is client port
1212 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
1213 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
1214
1215 udpPacket.setPayload(dhcpPayload);
1216 ipv4Packet.setPayload(udpPacket);
1217 etherReply.setPayload(ipv4Packet);
1218 return etherReply;
1219 }
1220 /**
Yi Tsengdcef2c22017-08-05 20:34:06 -07001221 * Extracts VLAN ID from relay agent option.
1222 *
1223 * @param dhcpPayload the DHCP payload
1224 * @return VLAN ID from DHCP payload; null if not exists
1225 */
Yi Tseng4f2a0462017-08-31 11:21:00 -07001226 private VlanId getVlanIdFromRelayAgentOption(DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001227 DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
1228 if (option == null) {
1229 return null;
1230 }
1231 DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
1232 if (circuitIdSubOption == null) {
1233 return null;
1234 }
1235 try {
1236 CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
1237 return circuitId.vlanId();
1238 } catch (IllegalArgumentException e) {
1239 // can't deserialize the circuit ID
1240 return null;
1241 }
1242 }
1243
1244 /**
1245 * Removes DHCP relay agent information option (option 82) from DHCP payload.
1246 * Also reset giaddr to 0
1247 *
1248 * @param ethPacket the Ethernet packet to be processed
1249 * @return Ethernet packet processed
1250 */
1251 private Ethernet removeRelayAgentOption(Ethernet ethPacket) {
rsahot036620655b2018-02-26 15:01:37 -05001252 Ethernet ethernet = (Ethernet) ethPacket.duplicate();
Yi Tsengdcef2c22017-08-05 20:34:06 -07001253 IPv4 ipv4 = (IPv4) ethernet.getPayload();
1254 UDP udp = (UDP) ipv4.getPayload();
1255 DHCP dhcpPayload = (DHCP) udp.getPayload();
1256
1257 // removes relay agent information option
1258 List<DhcpOption> options = dhcpPayload.getOptions();
1259 options = options.stream()
1260 .filter(option -> option.getCode() != OptionCode_CircuitID.getValue())
1261 .collect(Collectors.toList());
1262 dhcpPayload.setOptions(options);
1263 dhcpPayload.setGatewayIPAddress(0);
1264
1265 udp.setPayload(dhcpPayload);
1266 ipv4.setPayload(udp);
1267 ethernet.setPayload(ipv4);
1268 return ethernet;
1269 }
1270
Yi Tseng51301292017-07-28 13:02:59 -07001271
1272 /**
1273 * Check if the host is directly connected to the network or not.
1274 *
1275 * @param dhcpPayload the dhcp payload
1276 * @return true if the host is directly connected to the network; false otherwise
1277 */
1278 private boolean directlyConnected(DHCP dhcpPayload) {
Yi Tseng440e2b72017-08-24 14:47:34 -07001279 DhcpRelayAgentOption relayAgentOption =
1280 (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
Yi Tseng51301292017-07-28 13:02:59 -07001281
1282 // Doesn't contains relay option
1283 if (relayAgentOption == null) {
1284 return true;
1285 }
1286
Yi Tseng440e2b72017-08-24 14:47:34 -07001287 // check circuit id, if circuit id is invalid, we say it is an indirect host
1288 DhcpOption circuitIdOpt = relayAgentOption.getSubOption(CIRCUIT_ID.getValue());
Yi Tseng51301292017-07-28 13:02:59 -07001289
Yi Tseng440e2b72017-08-24 14:47:34 -07001290 try {
1291 CircuitId.deserialize(circuitIdOpt.getData());
Yi Tseng51301292017-07-28 13:02:59 -07001292 return true;
Yi Tseng440e2b72017-08-24 14:47:34 -07001293 } catch (Exception e) {
1294 // invalid circuit id
1295 return false;
Yi Tseng51301292017-07-28 13:02:59 -07001296 }
Yi Tseng51301292017-07-28 13:02:59 -07001297 }
1298
1299
1300 /**
1301 * Send the DHCP ack to the requester host.
1302 * Modify Host or Route store according to the type of DHCP.
1303 *
1304 * @param ethernetPacketAck the packet
1305 * @param dhcpPayload the DHCP data
1306 */
1307 private void handleDhcpAck(Ethernet ethernetPacketAck, DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001308 Optional<Interface> outInterface = getClientInterface(ethernetPacketAck, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -07001309 if (!outInterface.isPresent()) {
1310 log.warn("Can't find output interface for dhcp: {}", dhcpPayload);
1311 return;
1312 }
1313
1314 Interface outIface = outInterface.get();
1315 HostLocation hostLocation = new HostLocation(outIface.connectPoint(), System.currentTimeMillis());
1316 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
Yi Tseng4f2a0462017-08-31 11:21:00 -07001317 VlanId vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001318 if (vlanId == null) {
1319 vlanId = outIface.vlan();
1320 }
Yi Tseng51301292017-07-28 13:02:59 -07001321 HostId hostId = HostId.hostId(macAddress, vlanId);
1322 Ip4Address ip = Ip4Address.valueOf(dhcpPayload.getYourIPAddress());
1323
1324 if (directlyConnected(dhcpPayload)) {
1325 // Add to host store if it connect to network directly
1326 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tsengaa417a62017-09-08 17:22:51 -07001327 Host host = hostService.getHost(hostId);
Yi Tseng51301292017-07-28 13:02:59 -07001328
Yi Tsengaa417a62017-09-08 17:22:51 -07001329 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
1330 if (host != null) {
1331 // Dual homing support:
1332 // if host exists, use old locations and new location
1333 hostLocations.addAll(host.locations());
1334 }
1335 HostDescription desc = new DefaultHostDescription(macAddress, vlanId,
1336 hostLocations, ips, false);
1337 // Add IP address when dhcp server give the host new ip address
1338 providerService.hostDetected(hostId, desc, false);
Yi Tseng51301292017-07-28 13:02:59 -07001339 } else {
1340 // Add to route store if it does not connect to network directly
1341 // Get gateway host IP according to host mac address
Yi Tsengdcef2c22017-08-05 20:34:06 -07001342 // TODO: remove relay store here
Yi Tseng51301292017-07-28 13:02:59 -07001343 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1344
1345 if (record == null) {
1346 log.warn("Can't find DHCP record of host {}", hostId);
1347 return;
1348 }
1349
1350 MacAddress gwMac = record.nextHop().orElse(null);
1351 if (gwMac == null) {
1352 log.warn("Can't find gateway mac address from record {}", record);
1353 return;
1354 }
1355
1356 HostId gwHostId = HostId.hostId(gwMac, record.vlanId());
1357 Host gwHost = hostService.getHost(gwHostId);
1358
1359 if (gwHost == null) {
1360 log.warn("Can't find gateway host {}", gwHostId);
1361 return;
1362 }
1363
1364 Ip4Address nextHopIp = gwHost.ipAddresses()
1365 .stream()
1366 .filter(IpAddress::isIp4)
1367 .map(IpAddress::getIp4Address)
1368 .findFirst()
1369 .orElse(null);
1370
1371 if (nextHopIp == null) {
1372 log.warn("Can't find IP address of gateway {}", gwHost);
1373 return;
1374 }
1375
Charles Chan6305b692018-04-04 11:43:54 -07001376 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Yi Tseng51301292017-07-28 13:02:59 -07001377 routeStore.updateRoute(route);
1378 }
Yi Tseng51301292017-07-28 13:02:59 -07001379 }
1380
1381 /**
Yi Tseng51301292017-07-28 13:02:59 -07001382 * Gets output interface of a dhcp packet.
1383 * If option 82 exists in the dhcp packet and the option was sent by
Yi Tseng4f2a0462017-08-31 11:21:00 -07001384 * ONOS (circuit format is correct), use the connect
Yi Tseng51301292017-07-28 13:02:59 -07001385 * point and vlan id from circuit id; otherwise, find host by destination
1386 * address and use vlan id from sender (dhcp server).
1387 *
1388 * @param ethPacket the ethernet packet
1389 * @param dhcpPayload the dhcp packet
1390 * @return an interface represent the output port and vlan; empty value
1391 * if the host or circuit id not found
1392 */
Yi Tsengdcef2c22017-08-05 20:34:06 -07001393 private Optional<Interface> getClientInterface(Ethernet ethPacket, DHCP dhcpPayload) {
Yi Tseng51301292017-07-28 13:02:59 -07001394 VlanId originalPacketVlanId = VlanId.vlanId(ethPacket.getVlanID());
Yi Tseng51301292017-07-28 13:02:59 -07001395 DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
1396
Yi Tseng4f2a0462017-08-31 11:21:00 -07001397 DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
1398 try {
1399 CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
1400 ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(circuitId.connectPoint());
1401 VlanId vlanId = circuitId.vlanId();
1402 return interfaceService.getInterfacesByPort(connectPoint)
1403 .stream()
1404 .filter(iface -> interfaceContainsVlan(iface, vlanId))
1405 .findFirst();
1406 } catch (IllegalArgumentException ex) {
1407 // invalid circuit format, didn't sent by ONOS
1408 log.debug("Invalid circuit {}, use information from dhcp payload",
1409 circuitIdSubOption.getData());
Yi Tseng51301292017-07-28 13:02:59 -07001410 }
1411
1412 // Use Vlan Id from DHCP server if DHCP relay circuit id was not
1413 // sent by ONOS or circuit Id can't be parsed
Yi Tsengdcef2c22017-08-05 20:34:06 -07001414 // TODO: remove relay store from this method
Yi Tseng51301292017-07-28 13:02:59 -07001415 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
1416 Optional<DhcpRecord> dhcpRecord = dhcpRelayStore.getDhcpRecord(HostId.hostId(dstMac, originalPacketVlanId));
Yi Tsengdcef2c22017-08-05 20:34:06 -07001417 ConnectPoint clientConnectPoint = dhcpRecord
Yi Tseng51301292017-07-28 13:02:59 -07001418 .map(DhcpRecord::locations)
1419 .orElse(Collections.emptySet())
1420 .stream()
1421 .reduce((hl1, hl2) -> {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001422 // find latest host connect point
Yi Tseng51301292017-07-28 13:02:59 -07001423 if (hl1 == null || hl2 == null) {
1424 return hl1 == null ? hl2 : hl1;
1425 }
1426 return hl1.time() > hl2.time() ? hl1 : hl2;
1427 })
Yi Tsengdcef2c22017-08-05 20:34:06 -07001428 .orElse(null);
Yi Tseng51301292017-07-28 13:02:59 -07001429
Yi Tsengdcef2c22017-08-05 20:34:06 -07001430 if (clientConnectPoint != null) {
1431 return interfaceService.getInterfacesByPort(clientConnectPoint)
1432 .stream()
Yi Tseng4f2a0462017-08-31 11:21:00 -07001433 .filter(iface -> interfaceContainsVlan(iface, originalPacketVlanId))
Yi Tsengdcef2c22017-08-05 20:34:06 -07001434 .findFirst();
1435 }
1436 return Optional.empty();
Yi Tseng51301292017-07-28 13:02:59 -07001437 }
1438
1439 /**
1440 * Send the response DHCP to the requester host.
1441 *
1442 * @param ethPacket the packet
1443 * @param dhcpPayload the DHCP data
1444 */
1445 private void sendResponseToClient(Ethernet ethPacket, DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001446 Optional<Interface> outInterface = getClientInterface(ethPacket, dhcpPayload);
1447 if (directlyConnected(dhcpPayload)) {
1448 ethPacket = removeRelayAgentOption(ethPacket);
1449 }
1450 if (!outInterface.isPresent()) {
1451 log.warn("Can't find output interface for client, ignore");
1452 return;
1453 }
1454 Interface outIface = outInterface.get();
1455 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1456 .setOutput(outIface.connectPoint().port())
1457 .build();
1458 OutboundPacket o = new DefaultOutboundPacket(
1459 outIface.connectPoint().deviceId(),
1460 treatment,
1461 ByteBuffer.wrap(ethPacket.serialize()));
1462 if (log.isTraceEnabled()) {
1463 log.trace("Relaying packet to DHCP client {} via {}, vlan {}",
1464 ethPacket,
1465 outIface.connectPoint(),
1466 outIface.vlan());
1467 }
1468 packetService.emit(o);
Yi Tseng51301292017-07-28 13:02:59 -07001469 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001470
Yi Tsengaa417a62017-09-08 17:22:51 -07001471 @Override
1472 public void triggerProbe(Host host) {
1473 // Do nothing here
1474 }
1475
1476 @Override
1477 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -07001478 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -07001479 }
1480
Yi Tseng483ac6f2017-08-02 15:03:31 -07001481 class InternalHostListener implements HostListener {
1482 @Override
1483 public void event(HostEvent event) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001484 if (!configured()) {
1485 return;
1486 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001487 switch (event.type()) {
1488 case HOST_ADDED:
1489 case HOST_UPDATED:
1490 hostUpdated(event.subject());
1491 break;
1492 case HOST_REMOVED:
1493 hostRemoved(event.subject());
1494 break;
Yi Tseng483ac6f2017-08-02 15:03:31 -07001495 default:
1496 break;
1497 }
1498 }
1499 }
1500
1501 /**
Yi Tseng483ac6f2017-08-02 15:03:31 -07001502 * Handle host updated.
1503 * If the host is DHCP server or gateway, update connect mac and vlan.
1504 *
1505 * @param host the host
1506 */
1507 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001508 hostUpdated(host, defaultServerInfoList);
1509 hostUpdated(host, indirectServerInfoList);
Yi Tseng483ac6f2017-08-02 15:03:31 -07001510 }
1511
Yi Tseng525ff402017-10-23 19:39:39 -07001512 private void hostUpdated(Host host, List<DhcpServerInfo> srverInfoList) {
1513 DhcpServerInfo serverInfo;
1514 Ip4Address targetIp;
1515 if (!srverInfoList.isEmpty()) {
1516 serverInfo = srverInfoList.get(0);
1517 targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
1518 Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
1519
1520 if (targetIp == null) {
1521 targetIp = serverIp;
1522 }
1523
1524 if (targetIp != null) {
1525 if (host.ipAddresses().contains(targetIp)) {
1526 serverInfo.setDhcpConnectMac(host.mac());
1527 serverInfo.setDhcpConnectVlan(host.vlan());
1528 requestDhcpPacket(serverIp);
1529 }
1530 }
1531 }
1532 }
1533
1534
Yi Tseng483ac6f2017-08-02 15:03:31 -07001535 /**
1536 * Handle host removed.
1537 * If the host is DHCP server or gateway, unset connect mac and vlan.
1538 *
1539 * @param host the host
1540 */
1541 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001542 hostRemoved(host, defaultServerInfoList);
1543 hostRemoved(host, indirectServerInfoList);
1544 }
1545
1546 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001547 DhcpServerInfo serverInfo;
Yi Tseng525ff402017-10-23 19:39:39 -07001548 Ip4Address targetIp;
1549 if (!serverInfoList.isEmpty()) {
1550 serverInfo = serverInfoList.get(0);
1551 Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
1552 targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
Yi Tseng919b2df2017-09-07 16:22:51 -07001553
Yi Tseng525ff402017-10-23 19:39:39 -07001554 if (targetIp == null) {
1555 targetIp = serverIp;
Yi Tseng919b2df2017-09-07 16:22:51 -07001556 }
Yi Tseng525ff402017-10-23 19:39:39 -07001557
1558 if (targetIp != null) {
1559 if (host.ipAddresses().contains(targetIp)) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001560 serverInfo.setDhcpConnectVlan(null);
1561 serverInfo.setDhcpConnectMac(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001562 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001563 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001564 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001565 }
Yi Tseng525ff402017-10-23 19:39:39 -07001566 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001567
Yi Tseng525ff402017-10-23 19:39:39 -07001568 private void requestDhcpPacket(Ip4Address serverIp) {
1569 requestServerDhcpPacket(serverIp);
1570 requestClientDhcpPacket(serverIp);
1571 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001572
Yi Tseng525ff402017-10-23 19:39:39 -07001573 private void cancelDhcpPacket(Ip4Address serverIp) {
1574 cancelServerDhcpPacket(serverIp);
1575 cancelClientDhcpPacket(serverIp);
1576 }
1577
1578 private void cancelServerDhcpPacket(Ip4Address serverIp) {
1579 TrafficSelector serverSelector =
1580 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1581 .matchIPSrc(serverIp.toIpPrefix())
1582 .build();
1583 packetService.cancelPackets(serverSelector,
1584 PacketPriority.CONTROL,
1585 appId);
1586 }
1587
1588 private void requestServerDhcpPacket(Ip4Address serverIp) {
1589 TrafficSelector serverSelector =
1590 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1591 .matchIPSrc(serverIp.toIpPrefix())
1592 .build();
1593 packetService.requestPackets(serverSelector,
1594 PacketPriority.CONTROL,
1595 appId);
1596 }
1597
1598 private void cancelClientDhcpPacket(Ip4Address serverIp) {
1599 // Packet comes from relay
1600 TrafficSelector indirectClientSelector =
1601 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1602 .matchIPDst(serverIp.toIpPrefix())
1603 .build();
1604 packetService.cancelPackets(indirectClientSelector,
1605 PacketPriority.CONTROL,
1606 appId);
1607
1608 // Packet comes from client
1609 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1610 PacketPriority.CONTROL,
1611 appId);
1612 }
1613
1614 private void requestClientDhcpPacket(Ip4Address serverIp) {
1615 // Packet comes from relay
1616 TrafficSelector indirectClientSelector =
1617 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1618 .matchIPDst(serverIp.toIpPrefix())
1619 .build();
1620 packetService.requestPackets(indirectClientSelector,
1621 PacketPriority.CONTROL,
1622 appId);
1623
1624 // Packet comes from client
1625 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1626 PacketPriority.CONTROL,
1627 appId);
1628 }
1629
1630 /**
1631 * Process the ignore rules.
1632 *
1633 * @param deviceId the device id
1634 * @param vlanId the vlan to be ignored
1635 * @param op the operation, ADD to install; REMOVE to uninstall rules
1636 */
1637 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001638 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1639 DHCP_SELECTORS.forEach(trafficSelector -> {
1640 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1641 .matchVlanId(vlanId)
1642 .build();
1643
1644 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1645 .withFlag(ForwardingObjective.Flag.VERSATILE)
1646 .withSelector(selector)
1647 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001648 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001649 .fromApp(appId);
1650
1651
1652 ObjectiveContext objectiveContext = new ObjectiveContext() {
1653 @Override
1654 public void onSuccess(Objective objective) {
1655 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1656 op, vlanId, deviceId, selector);
1657 int countDown = installedCount.decrementAndGet();
1658 if (countDown != 0) {
1659 return;
1660 }
1661 switch (op) {
1662 case ADD:
1663 ignoredVlans.put(deviceId, vlanId);
1664 break;
1665 case REMOVE:
1666 ignoredVlans.remove(deviceId, vlanId);
1667 break;
1668 default:
1669 log.warn("Unsupported objective operation {}", op);
1670 break;
1671 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001672 }
Yi Tseng525ff402017-10-23 19:39:39 -07001673
1674 @Override
1675 public void onError(Objective objective, ObjectiveError error) {
1676 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1677 op, vlanId, selector, deviceId, error);
Yi Tseng919b2df2017-09-07 16:22:51 -07001678 }
Yi Tseng525ff402017-10-23 19:39:39 -07001679 };
1680
1681 ForwardingObjective fwd;
1682 switch (op) {
1683 case ADD:
1684 fwd = builder.add(objectiveContext);
1685 break;
1686 case REMOVE:
1687 fwd = builder.remove(objectiveContext);
1688 break;
1689 default:
1690 log.warn("Unsupported objective operation {}", op);
1691 return;
Yi Tseng4f2a0462017-08-31 11:21:00 -07001692 }
Yi Tseng525ff402017-10-23 19:39:39 -07001693
1694 Device device = deviceService.getDevice(deviceId);
1695 if (device == null || !device.is(Pipeliner.class)) {
1696 log.warn("Device {} is not available now, wait until device is available", deviceId);
1697 return;
1698 }
1699 flowObjectiveService.apply(deviceId, fwd);
1700 });
Yi Tseng483ac6f2017-08-02 15:03:31 -07001701 }
Kalhee Kimba366062017-11-07 16:32:09 +00001702
1703 @Override
1704 public void setDhcpFpmEnabled(Boolean enabled) {
1705 // v4 does not use fpm. Do nothing.
1706 }
rsahot036620655b2018-02-26 15:01:37 -05001707 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1708 List<DhcpServerInfo> validServerInfo;
1709
1710 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1711 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1712 } else {
1713 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1714 }
1715 return validServerInfo;
1716 }
1717
1718
1719 private boolean checkDhcpServerConnPt(boolean directConnFlag,
1720 DhcpServerInfo serverInfo) {
1721 if (serverInfo.getDhcpServerConnectPoint() == null) {
1722 log.warn("DHCP4 server connect point for {} connPt {}",
1723 directConnFlag ? "direct" : "indirect", serverInfo.getDhcpServerConnectPoint());
1724 return false;
1725 }
1726 return true;
1727 }
1728
1729 /**
1730 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1731 *
1732 * @param serverInfo server information
1733 * @return newServerInfo if host info can be either found or filled in.
1734 */
1735 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1736 DhcpServerInfo newServerInfo = null;
1737 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1738 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1739 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1740
1741 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1742 newServerInfo = serverInfo;
Charles Chan2de55302018-03-02 18:27:59 -08001743 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp4(),
rsahot036620655b2018-02-26 15:01:37 -05001744 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1745 } else {
1746 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp4(),
1747 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1748
1749 Ip4Address ipToProbe;
1750 if (serverInfo.getDhcpGatewayIp4().isPresent()) {
1751 ipToProbe = serverInfo.getDhcpGatewayIp4().get();
1752 } else {
1753 ipToProbe = serverInfo.getDhcpServerIp4().orElse(null);
1754 }
1755 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1756 .map(ip -> "gateway").orElse("server");
1757
1758 log.warn("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1759 hostService.startMonitoringIp(ipToProbe);
1760
1761 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1762 if (!hosts.isEmpty()) {
1763 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1764 Host host = hosts.iterator().next();
1765 serverInfo.setDhcpConnectVlan(host.vlan());
1766 serverInfo.setDhcpConnectMac(host.mac());
1767 // replace the serverInfo in the list
1768 sererInfoList.set(serverInfoIndex, serverInfo);
1769 newServerInfo = serverInfo;
1770 log.warn("Dynamically host found host {}", host);
1771 } else {
1772 log.warn("No host found host ip {} dynamically", ipToProbe);
1773 }
1774 }
1775 return newServerInfo;
1776 }
1777
1778 /**
1779 * Gets Interface facing to the server for default host.
1780 *
1781 * @param serverInfo server information
1782 * @return the Interface facing to the server; null if not found
1783 */
1784 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1785 Interface serverInterface = null;
1786
1787 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1788 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1789
1790 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1791 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1792 .stream()
1793 .filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1794 .findFirst()
1795 .orElse(null);
1796 } else {
1797 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1798 dhcpServerConnectPoint, dhcpConnectVlan);
1799 }
1800
1801 return serverInterface;
1802 }
1803
1804 //forward the packet to ConnectPoint where the DHCP server is attached.
1805 private void forwardPacket(InternalPacket packet) {
1806 //send Packetout to dhcp server connectpoint.
1807 if (packet.destLocation != null) {
1808 TrafficTreatment t = DefaultTrafficTreatment.builder()
1809 .setOutput(packet.destLocation.port()).build();
1810 OutboundPacket o = new DefaultOutboundPacket(
1811 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
1812 if (log.isTraceEnabled()) {
1813 log.trace("Relaying packet to destination {}", packet.destLocation);
1814 }
Charles Chan2de55302018-03-02 18:27:59 -08001815 log.debug("packetService.emit(o) to port {}", packet.destLocation);
rsahot036620655b2018-02-26 15:01:37 -05001816 packetService.emit(o);
1817 }
1818 }
1819
1820
1821 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1822 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1823 DhcpServerInfo foundServerInfo = null;
1824 for (DhcpServerInfo serverInfo : validServerInfoList) {
1825 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1826 foundServerInfo = serverInfo;
Charles Chan2de55302018-03-02 18:27:59 -08001827 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
rsahot036620655b2018-02-26 15:01:37 -05001828 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1829 break;
1830 } else {
1831 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1832 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1833 }
1834 }
1835 return foundServerInfo;
1836 }
1837
Yi Tseng51301292017-07-28 13:02:59 -07001838}