blob: 731090dd3396fba84bc0264235b0d4a230788b7d [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;
Yi Tseng525ff402017-10-23 19:39:39 -070022import com.google.common.collect.Multimap;
Yi Tseng51301292017-07-28 13:02:59 -070023import com.google.common.collect.Sets;
Yi Tseng483ac6f2017-08-02 15:03:31 -070024import org.apache.felix.scr.annotations.Activate;
Yi Tseng51301292017-07-28 13:02:59 -070025import org.apache.felix.scr.annotations.Component;
Yi Tseng483ac6f2017-08-02 15:03:31 -070026import org.apache.felix.scr.annotations.Deactivate;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Property;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.apache.felix.scr.annotations.Service;
31import org.onlab.packet.BasePacket;
32import org.onlab.packet.DHCP;
33import org.onlab.packet.Ethernet;
34import org.onlab.packet.IPv4;
35import org.onlab.packet.Ip4Address;
36import org.onlab.packet.IpAddress;
37import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070038import org.onlab.packet.TpPort;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.UDP;
40import org.onlab.packet.VlanId;
41import org.onlab.packet.dhcp.CircuitId;
42import org.onlab.packet.dhcp.DhcpOption;
43import org.onlab.packet.dhcp.DhcpRelayAgentOption;
Yi Tseng525ff402017-10-23 19:39:39 -070044import org.onosproject.core.ApplicationId;
45import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070046import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070047import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng483ac6f2017-08-02 15:03:31 -070048import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng525ff402017-10-23 19:39:39 -070049import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Yi Tseng51301292017-07-28 13:02:59 -070050import org.onosproject.dhcprelay.store.DhcpRecord;
51import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng525ff402017-10-23 19:39:39 -070052import org.onosproject.net.Device;
53import org.onosproject.net.DeviceId;
54import org.onosproject.net.behaviour.Pipeliner;
55import org.onosproject.net.device.DeviceService;
56import org.onosproject.net.flow.DefaultTrafficSelector;
57import org.onosproject.net.flow.TrafficSelector;
58import org.onosproject.net.flowobjective.DefaultForwardingObjective;
59import org.onosproject.net.flowobjective.FlowObjectiveService;
60import org.onosproject.net.flowobjective.ForwardingObjective;
61import org.onosproject.net.flowobjective.Objective;
62import org.onosproject.net.flowobjective.ObjectiveContext;
63import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng483ac6f2017-08-02 15:03:31 -070064import org.onosproject.net.host.HostEvent;
65import org.onosproject.net.host.HostListener;
Yi Tsengaa417a62017-09-08 17:22:51 -070066import org.onosproject.net.host.HostProvider;
67import org.onosproject.net.host.HostProviderRegistry;
68import org.onosproject.net.host.HostProviderService;
Ray Milkeyfacf2862017-08-03 11:58:29 -070069import org.onosproject.net.intf.Interface;
70import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070071import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070072import org.onosproject.net.provider.ProviderId;
Ray Milkey69ec8712017-08-08 13:00:43 -070073import org.onosproject.routeservice.Route;
74import org.onosproject.routeservice.RouteStore;
Yi Tseng51301292017-07-28 13:02:59 -070075import org.onosproject.net.ConnectPoint;
76import org.onosproject.net.Host;
77import org.onosproject.net.HostId;
78import org.onosproject.net.HostLocation;
79import org.onosproject.net.flow.DefaultTrafficTreatment;
80import org.onosproject.net.flow.TrafficTreatment;
81import org.onosproject.net.host.DefaultHostDescription;
82import org.onosproject.net.host.HostDescription;
83import org.onosproject.net.host.HostService;
Yi Tseng51301292017-07-28 13:02:59 -070084import org.onosproject.net.host.InterfaceIpAddress;
85import org.onosproject.net.packet.DefaultOutboundPacket;
86import org.onosproject.net.packet.OutboundPacket;
87import org.onosproject.net.packet.PacketContext;
88import org.onosproject.net.packet.PacketService;
89import org.slf4j.Logger;
90import org.slf4j.LoggerFactory;
91
92import java.nio.ByteBuffer;
Yi Tsengdcef2c22017-08-05 20:34:06 -070093import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -070094import java.util.Collections;
95import java.util.List;
rsahot036620655b2018-02-26 15:01:37 -050096import java.util.ArrayList;
Yi Tseng51301292017-07-28 13:02:59 -070097import java.util.Optional;
98import java.util.Set;
Charles Chan909cff82018-03-05 13:14:02 -080099import java.util.concurrent.CopyOnWriteArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700100import java.util.concurrent.atomic.AtomicInteger;
Yi Tseng51301292017-07-28 13:02:59 -0700101import java.util.stream.Collectors;
102
103import static com.google.common.base.Preconditions.checkNotNull;
104import static com.google.common.base.Preconditions.checkState;
105import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
106import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
107import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
108import static org.onlab.packet.MacAddress.valueOf;
109import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID;
Yi Tseng525ff402017-10-23 19:39:39 -0700110import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
111import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700112
rsahot036620655b2018-02-26 15:01:37 -0500113import org.onosproject.dhcprelay.Dhcp4HandlerUtil.InternalPacket;
114
Yi Tseng51301292017-07-28 13:02:59 -0700115@Component
116@Service
117@Property(name = "version", value = "4")
Yi Tsengaa417a62017-09-08 17:22:51 -0700118public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700119 public static final String DHCP_V4_RELAY_APP = "org.onosproject.Dhcp4HandlerImpl";
120 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp4", DHCP_V4_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700121 private static final String BROADCAST_IP = "255.255.255.255";
122 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
123
124 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
125 .matchEthType(Ethernet.TYPE_IPV4)
126 .matchIPProtocol(IPv4.PROTOCOL_UDP)
127 .matchIPSrc(Ip4Address.ZERO.toIpPrefix())
128 .matchIPDst(Ip4Address.valueOf(BROADCAST_IP).toIpPrefix())
129 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
130 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
131 .build();
132 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
133 .matchEthType(Ethernet.TYPE_IPV4)
134 .matchIPProtocol(IPv4.PROTOCOL_UDP)
135 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
136 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
137 .build();
138 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
139 CLIENT_SERVER_SELECTOR,
140 SERVER_RELAY_SELECTOR
141 );
Yi Tseng51301292017-07-28 13:02:59 -0700142 private static Logger log = LoggerFactory.getLogger(Dhcp4HandlerImpl.class);
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected DhcpRelayStore dhcpRelayStore;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected PacketService packetService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng51301292017-07-28 13:02:59 -0700151 protected RouteStore routeStore;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected InterfaceService interfaceService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
157 protected HostService hostService;
158
Yi Tsengaa417a62017-09-08 17:22:51 -0700159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected HostProviderRegistry providerRegistry;
161
Yi Tseng525ff402017-10-23 19:39:39 -0700162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected CoreService coreService;
164
165 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
166 protected DeviceService deviceService;
167
168 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
169 protected FlowObjectiveService flowObjectiveService;
170
Yi Tsengaa417a62017-09-08 17:22:51 -0700171 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700172 protected ApplicationId appId;
173 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
Yi Tseng483ac6f2017-08-02 15:03:31 -0700174 private InternalHostListener hostListener = new InternalHostListener();
175
Charles Chan909cff82018-03-05 13:14:02 -0800176 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
177 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
rsahot036620655b2018-02-26 15:01:37 -0500178 private Dhcp4HandlerUtil dhcp4HandlerUtil = new Dhcp4HandlerUtil();
Yi Tseng4f2a0462017-08-31 11:21:00 -0700179
Yi Tseng483ac6f2017-08-02 15:03:31 -0700180 @Activate
181 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700182 appId = coreService.registerApplication(DHCP_V4_RELAY_APP);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700183 hostService.addListener(hostListener);
Yi Tsengaa417a62017-09-08 17:22:51 -0700184 providerService = providerRegistry.register(this);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700185 }
186
187 @Deactivate
188 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700189 providerRegistry.unregister(this);
190 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700191 defaultServerInfoList.forEach(this::stopMonitoringIps);
192 defaultServerInfoList.clear();
193 indirectServerInfoList.forEach(this::stopMonitoringIps);
194 indirectServerInfoList.clear();
Yi Tseng483ac6f2017-08-02 15:03:31 -0700195 }
196
Yi Tseng919b2df2017-09-07 16:22:51 -0700197 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
198 serverInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
199 hostService.stopMonitoringIp(gatewayIp);
200 });
201 serverInfo.getDhcpServerIp4().ifPresent(serverIp -> {
202 hostService.stopMonitoringIp(serverIp);
203 });
Yi Tseng51301292017-07-28 13:02:59 -0700204 }
205
206 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700207 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -0700208 setDhcpServerConfigs(configs, defaultServerInfoList);
209 }
210
211 @Override
212 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
213 setDhcpServerConfigs(configs, indirectServerInfoList);
214 }
215
216 @Override
217 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
218 return defaultServerInfoList;
219 }
220
221 @Override
222 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
223 return indirectServerInfoList;
224 }
225
Yi Tseng525ff402017-10-23 19:39:39 -0700226 @Override
227 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
228 if (config == null) {
229 ignoredVlans.forEach(((deviceId, vlanId) -> {
230 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
231 }));
232 return;
233 }
234 config.ignoredVlans().forEach((deviceId, vlanId) -> {
235 if (ignoredVlans.get(deviceId).contains(vlanId)) {
236 // don't need to process if it already ignored
237 return;
238 }
239 processIgnoreVlanRule(deviceId, vlanId, ADD);
240 });
241
242 ignoredVlans.forEach((deviceId, vlanId) -> {
243 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
244 // not contains in new config, remove it
245 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
246 }
247 });
248 }
249
Saurav Dasb805f1a2017-12-13 16:19:35 -0800250 @Override
251 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
252 if (config == null) {
253 ignoredVlans.clear();
254 return;
255 }
256 config.ignoredVlans().forEach((deviceId, vlanId) -> {
257 ignoredVlans.remove(deviceId, vlanId);
258 });
259 }
260
Yi Tseng919b2df2017-09-07 16:22:51 -0700261 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700262 if (configs.size() == 0) {
263 // no config to update
264 return;
265 }
266
rsahot036620655b2018-02-26 15:01:37 -0500267 Boolean isConfigValid = false;
268 for (DhcpServerConfig serverConfig : configs) {
269 if (serverConfig.getDhcpServerIp4().isPresent()) {
270 isConfigValid = true;
271 break;
272 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700273 }
rsahot036620655b2018-02-26 15:01:37 -0500274 if (!isConfigValid) {
275 log.warn("No IP V4 server address found.");
276 return; // No IP V6 address found
277 }
278 // if (!serverInfoList.isEmpty()) {
279 for (DhcpServerInfo oldServerInfo : serverInfoList) {
280 log.info("In for (DhcpServerInfo oldServerInfo : serverInfoList) {");
Yi Tseng919b2df2017-09-07 16:22:51 -0700281 // remove old server info
rsahot036620655b2018-02-26 15:01:37 -0500282 //DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700283
Yi Tseng919b2df2017-09-07 16:22:51 -0700284 // stop monitoring gateway or server
285 oldServerInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
286 hostService.stopMonitoringIp(gatewayIp);
287 });
288 oldServerInfo.getDhcpServerIp4().ifPresent(serverIp -> {
289 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -0700290 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -0700291 });
Yi Tseng483ac6f2017-08-02 15:03:31 -0700292 }
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700293
Yi Tseng919b2df2017-09-07 16:22:51 -0700294 // Create new server info according to the config
rsahot036620655b2018-02-26 15:01:37 -0500295 serverInfoList.clear();
296 for (DhcpServerConfig serverConfig : configs) {
297 log.info("// Create new server info according to the config");
298 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
299 DhcpServerInfo.Version.DHCP_V4);
300 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
301 "Connect point not exists");
302 checkState(newServerInfo.getDhcpServerIp4().isPresent(),
303 "IP of DHCP server not exists");
Yi Tseng4f2a0462017-08-31 11:21:00 -0700304
rsahot036620655b2018-02-26 15:01:37 -0500305 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
306 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -0700307
rsahot036620655b2018-02-26 15:01:37 -0500308 Ip4Address serverIp = newServerInfo.getDhcpServerIp4().get();
309 Ip4Address ipToProbe;
310 if (newServerInfo.getDhcpGatewayIp4().isPresent()) {
311 ipToProbe = newServerInfo.getDhcpGatewayIp4().get();
312 } else {
313 ipToProbe = newServerInfo.getDhcpServerIp4().orElse(null);
314 }
315 log.info("Probe_IP {}", ipToProbe);
316 String hostToProbe = newServerInfo.getDhcpGatewayIp4()
317 .map(ip -> "gateway").orElse("server");
318
319 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
320 hostService.startMonitoringIp(ipToProbe);
321
322 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
323 if (!hosts.isEmpty()) {
324 Host host = hosts.iterator().next();
325 newServerInfo.setDhcpConnectVlan(host.vlan());
326 newServerInfo.setDhcpConnectMac(host.mac());
327 }
328
329 // Add new server info
330 synchronized (this) {
331 //serverInfoList.clear();
332 serverInfoList.add(newServerInfo);
333 }
334
335 requestDhcpPacket(serverIp);
Yi Tseng4f2a0462017-08-31 11:21:00 -0700336 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700337 }
338
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700339 @Override
Yi Tseng51301292017-07-28 13:02:59 -0700340 public void processDhcpPacket(PacketContext context, BasePacket payload) {
341 checkNotNull(payload, "DHCP payload can't be null");
342 checkState(payload instanceof DHCP, "Payload is not a DHCP");
343 DHCP dhcpPayload = (DHCP) payload;
344 if (!configured()) {
Yi Tseng919b2df2017-09-07 16:22:51 -0700345 log.warn("Missing default DHCP relay server config. Abort packet processing");
Yi Tseng51301292017-07-28 13:02:59 -0700346 return;
347 }
348
349 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tseng51301292017-07-28 13:02:59 -0700350 checkNotNull(dhcpPayload, "Can't find DHCP payload");
351 Ethernet packet = context.inPacket().parsed();
352 DHCP.MsgType incomingPacketType = dhcpPayload.getOptions().stream()
353 .filter(dhcpOption -> dhcpOption.getCode() == OptionCode_MessageType.getValue())
354 .map(DhcpOption::getData)
355 .map(data -> DHCP.MsgType.getType(data[0]))
356 .findFirst()
357 .orElse(null);
358 checkNotNull(incomingPacketType, "Can't get message type from DHCP payload {}", dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500359 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
360 //ignore the packets if dhcp client interface is not configured on onos.
361 if (receivingInterfaces.isEmpty()) {
362 log.warn("Virtual interface is not configured on {}", inPort);
363 return;
364 }
Yi Tseng51301292017-07-28 13:02:59 -0700365 switch (incomingPacketType) {
366 case DHCPDISCOVER:
Yi Tsengdcef2c22017-08-05 20:34:06 -0700367 // Add the gateway IP as virtual interface IP for server to understand
Yi Tseng51301292017-07-28 13:02:59 -0700368 // the lease to be assigned and forward the packet to dhcp server.
rsahot036620655b2018-02-26 15:01:37 -0500369 List<InternalPacket> ethernetClientPacket =
370 processDhcpPacketFromClient(context, packet, receivingInterfaces);
371 for (InternalPacket internalPacket : ethernetClientPacket) {
372 log.debug("DHCPDISCOVER from {} Forward to server", inPort);
Yi Tseng51301292017-07-28 13:02:59 -0700373 writeRequestDhcpRecord(inPort, packet, dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500374 forwardPacket(internalPacket);
Yi Tseng51301292017-07-28 13:02:59 -0700375 }
376 break;
377 case DHCPOFFER:
378 //reply to dhcp client.
rsahot036620655b2018-02-26 15:01:37 -0500379 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(context, packet);
Yi Tseng51301292017-07-28 13:02:59 -0700380 if (ethernetPacketOffer != null) {
381 writeResponseDhcpRecord(ethernetPacketOffer, dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700382 sendResponseToClient(ethernetPacketOffer, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -0700383 }
384 break;
385 case DHCPREQUEST:
386 // add the gateway ip as virtual interface ip for server to understand
387 // the lease to be assigned and forward the packet to dhcp server.
rsahot036620655b2018-02-26 15:01:37 -0500388 List<InternalPacket> ethernetPacketRequest =
389 processDhcpPacketFromClient(context, packet, receivingInterfaces);
390 for (InternalPacket internalPacket : ethernetPacketRequest) {
391 log.debug("DHCPDISCOVER from {} Forward to server", inPort);
Yi Tseng51301292017-07-28 13:02:59 -0700392 writeRequestDhcpRecord(inPort, packet, dhcpPayload);
rsahot036620655b2018-02-26 15:01:37 -0500393 forwardPacket(internalPacket);
Yi Tseng51301292017-07-28 13:02:59 -0700394 }
395 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400396 case DHCPDECLINE:
397 break;
Yi Tseng51301292017-07-28 13:02:59 -0700398 case DHCPACK:
399 // reply to dhcp client.
rsahot036620655b2018-02-26 15:01:37 -0500400 Ethernet ethernetPacketAck = processDhcpPacketFromServer(context, packet);
Yi Tseng51301292017-07-28 13:02:59 -0700401 if (ethernetPacketAck != null) {
402 writeResponseDhcpRecord(ethernetPacketAck, dhcpPayload);
403 handleDhcpAck(ethernetPacketAck, dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700404 sendResponseToClient(ethernetPacketAck, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -0700405 }
406 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400407 case DHCPNAK:
408 break;
Yi Tseng51301292017-07-28 13:02:59 -0700409 case DHCPRELEASE:
410 // TODO: release the ip address from client
411 break;
Charles Chand0dd7002017-10-08 23:53:36 -0400412 case DHCPINFORM:
413 break;
414 case DHCPFORCERENEW:
415 break;
416 case DHCPLEASEQUERY:
417 handleLeaseQueryMsg(context, packet, dhcpPayload);
418 break;
419 case DHCPLEASEACTIVE:
420 handleLeaseQueryActivateMsg(packet, dhcpPayload);
421 break;
422 case DHCPLEASEUNASSIGNED:
423 case DHCPLEASEUNKNOWN:
424 handleLeaseQueryUnknown(packet, dhcpPayload);
425 break;
Yi Tseng51301292017-07-28 13:02:59 -0700426 default:
427 break;
428 }
429 }
430
431 /**
432 * Checks if this app has been configured.
433 *
434 * @return true if all information we need have been initialized
435 */
Yi Tseng4f2a0462017-08-31 11:21:00 -0700436 private boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700437 return !defaultServerInfoList.isEmpty();
Yi Tseng51301292017-07-28 13:02:59 -0700438 }
439
440 /**
Yi Tsengdcef2c22017-08-05 20:34:06 -0700441 * Returns the first interface ip from interface.
Yi Tseng51301292017-07-28 13:02:59 -0700442 *
Yi Tsengdcef2c22017-08-05 20:34:06 -0700443 * @param iface interface of one connect point
Yi Tseng51301292017-07-28 13:02:59 -0700444 * @return the first interface IP; null if not exists an IP address in
445 * these interfaces
446 */
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700447 private Ip4Address getFirstIpFromInterface(Interface iface) {
Yi Tsengdcef2c22017-08-05 20:34:06 -0700448 checkNotNull(iface, "Interface can't be null");
449 return iface.ipAddressesList().stream()
Yi Tseng51301292017-07-28 13:02:59 -0700450 .map(InterfaceIpAddress::ipAddress)
451 .filter(IpAddress::isIp4)
452 .map(IpAddress::getIp4Address)
453 .findFirst()
454 .orElse(null);
455 }
456
457 /**
Yi Tseng4f2a0462017-08-31 11:21:00 -0700458 * Gets Interface facing to the server for default host.
Yi Tsengdcef2c22017-08-05 20:34:06 -0700459 *
460 * @return the Interface facing to the server; null if not found
461 */
Yi Tseng919b2df2017-09-07 16:22:51 -0700462 private Interface getDefaultServerInterface() {
463 return getServerInterface(defaultServerInfoList);
Yi Tsengdcef2c22017-08-05 20:34:06 -0700464 }
465
466 /**
Yi Tseng4f2a0462017-08-31 11:21:00 -0700467 * Gets Interface facing to the server for indirect hosts.
468 * Use default server Interface if indirect server not configured.
469 *
470 * @return the Interface facing to the server; null if not found
471 */
472 private Interface getIndirectServerInterface() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700473 return getServerInterface(indirectServerInfoList);
474 }
475
476 private Interface getServerInterface(List<DhcpServerInfo> serverInfos) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800477 return serverInfos.stream()
Yi Tseng4f2a0462017-08-31 11:21:00 -0700478 .findFirst()
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800479 .map(serverInfo -> {
480 ConnectPoint dhcpServerConnectPoint =
481 serverInfo.getDhcpServerConnectPoint().orElse(null);
482 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
483 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
484 return null;
485 }
486 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
487 .stream()
488 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
489 .findFirst()
490 .orElse(null);
491 })
Yi Tseng4f2a0462017-08-31 11:21:00 -0700492 .orElse(null);
493 }
494
495 /**
496 * Determind if an Interface contains a vlan id.
497 *
498 * @param iface the Interface
499 * @param vlanId the vlan id
500 * @return true if the Interface contains the vlan id
501 */
502 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
Yi Tseng4025a102017-09-30 11:35:42 +0800503 if (vlanId.equals(VlanId.NONE)) {
504 // untagged packet, check if vlan untagged or vlan native is not NONE
505 return !iface.vlanUntagged().equals(VlanId.NONE) ||
506 !iface.vlanNative().equals(VlanId.NONE);
507 }
508 // tagged packet, check if the interface contains the vlan
509 return iface.vlanTagged().contains(vlanId);
Yi Tseng4f2a0462017-08-31 11:21:00 -0700510 }
511
Charles Chand0dd7002017-10-08 23:53:36 -0400512 private void handleLeaseQueryActivateMsg(Ethernet packet, DHCP dhcpPayload) {
513 log.debug("LQ: Got DHCPLEASEACTIVE packet!");
514
515 // TODO: release the ip address from client
516 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
517 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
518 HostId hostId = HostId.hostId(clientMacAddress, vlanId);
519 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
520
521 if (record == null) {
522 log.warn("Can't find record for host {} when processing DHCPLEASEACTIVE", hostId);
523 return;
524 }
525
526 // need to update routes
527 log.debug("Lease Query for Client results in DHCPLEASEACTIVE - route needs to be modified");
528 // get current route
529 // find the ip of that client with the DhcpRelay store
530
531 Ip4Address clientIP = record.ip4Address().orElse(null);
532 log.debug("LQ: IP of host is " + clientIP.getIp4Address());
533
534 MacAddress nextHopMac = record.nextHop().orElse(null);
535 log.debug("LQ: MAC of resulting *OLD* NH for that host is " + nextHopMac.toString());
536
537 // find the new NH by looking at the src MAC of the dhcp request
538 // from the LQ store
539 MacAddress newNextHopMac = record.nextHopTemp().orElse(null);
540 log.debug("LQ: MAC of resulting *NEW* NH for that host is " + newNextHopMac.toString());
541
542 log.debug("LQ: updating dhcp relay record with new NH");
543 record.nextHop(newNextHopMac);
544
545 // find the next hop IP from its mac
546 HostId gwHostId = HostId.hostId(newNextHopMac, vlanId);
547 Host gwHost = hostService.getHost(gwHostId);
548
549 if (gwHost == null) {
550 log.warn("Can't find gateway for new NH host " + gwHostId);
551 return;
552 }
553
554 Ip4Address nextHopIp = gwHost.ipAddresses()
555 .stream()
556 .filter(IpAddress::isIp4)
557 .map(IpAddress::getIp4Address)
558 .findFirst()
559 .orElse(null);
560
561 if (nextHopIp == null) {
562 log.warn("Can't find IP address of gateway " + gwHost);
563 return;
564 }
565
566 log.debug("LQ: *NEW* NH IP for host is " + nextHopIp.getIp4Address());
567 Route route = new Route(Route.Source.STATIC, clientIP.toIpPrefix(), nextHopIp);
568 routeStore.updateRoute(route);
569
570 // and forward to client
571 Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
572 if (ethernetPacket != null) {
573 sendResponseToClient(ethernetPacket, dhcpPayload);
574 }
575 }
576
577 /**
578 *
579 */
580 private void handleLeaseQueryMsg(PacketContext context, Ethernet packet, DHCP dhcpPayload) {
581 log.debug("LQ: Got DHCPLEASEQUERY packet!");
582 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
583 log.debug("LQ: got DHCPLEASEQUERY with MAC " + clientMacAddress.toString());
584 // add the client mac (hostid) of this request to a store (the entry will be removed with
585 // the reply sent to the originator)
586 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
587 HostId hId = HostId.hostId(clientMacAddress, vlanId);
588 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hId).orElse(null);
589 if (record != null) {
590 //new NH is to be taken from src mac of LQ packet
591 MacAddress newNextHop = packet.getSourceMAC();
592 record.nextHopTemp(newNextHop);
593 record.ip4Status(dhcpPayload.getPacketType());
594 record.updateLastSeen();
595
596 // do a basic routing of the packet (this is unicast routing
597 // not a relay operation like for other broadcast dhcp packets
rsahot036620655b2018-02-26 15:01:37 -0500598 List<InternalPacket> ethernetPacketRequest = processLeaseQueryFromAgent(context, packet);
Charles Chand0dd7002017-10-08 23:53:36 -0400599 // and forward to server
rsahot036620655b2018-02-26 15:01:37 -0500600 for (InternalPacket internalPacket : ethernetPacketRequest) {
601 log.debug("LeaseQueryMsg forward to server");
602 forwardPacket(internalPacket);
603 }
Charles Chand0dd7002017-10-08 23:53:36 -0400604 } else {
605 log.warn("LQ: Error! - DHCP relay record for that client not found - ignoring LQ!");
606 }
607 }
608
609 private void handleLeaseQueryUnknown(Ethernet packet, DHCP dhcpPayload) {
610 log.debug("Lease Query for Client results in DHCPLEASEUNASSIGNED or " +
611 "DHCPLEASEUNKNOWN - removing route & forwarding reply to originator");
612 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
613 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
614 HostId hostId = HostId.hostId(clientMacAddress, vlanId);
615 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
616
617 if (record == null) {
618 log.warn("Can't find record for host {} when handling LQ UNKNOWN/UNASSIGNED message", hostId);
619 return;
620 }
621
622 Ip4Address clientIP = record.ip4Address().orElse(null);
623 log.debug("LQ: IP of host is " + clientIP.getIp4Address());
624
625 // find the new NH by looking at the src MAC of the dhcp request
626 // from the LQ store
627 MacAddress nextHopMac = record.nextHop().orElse(null);
628 log.debug("LQ: MAC of resulting *Existing* NH for that route is " + nextHopMac.toString());
629
630 // find the next hop IP from its mac
631 HostId gwHostId = HostId.hostId(nextHopMac, vlanId);
632 Host gwHost = hostService.getHost(gwHostId);
633
634 if (gwHost == null) {
635 log.warn("Can't find gateway for new NH host " + gwHostId);
636 return;
637 }
638
639 Ip4Address nextHopIp = gwHost.ipAddresses()
640 .stream()
641 .filter(IpAddress::isIp4)
642 .map(IpAddress::getIp4Address)
643 .findFirst()
644 .orElse(null);
645
646 if (nextHopIp == null) {
647 log.warn("Can't find IP address of gateway {}", gwHost);
648 return;
649 }
650
651 log.debug("LQ: *Existing* NH IP for host is " + nextHopIp.getIp4Address() + " removing route for it");
652 Route route = new Route(Route.Source.STATIC, clientIP.toIpPrefix(), nextHopIp);
653 routeStore.removeRoute(route);
654
655 // remove from temp store
656 dhcpRelayStore.removeDhcpRecord(hostId);
657
658 // and forward to client
659 Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
660 if (ethernetPacket != null) {
661 sendResponseToClient(ethernetPacket, dhcpPayload);
662 }
663 }
664
Yi Tseng4f2a0462017-08-31 11:21:00 -0700665 /**
Yi Tseng51301292017-07-28 13:02:59 -0700666 * Build the DHCP discover/request packet with gateway IP(unicast packet).
667 *
668 * @param context the packet context
669 * @param ethernetPacket the ethernet payload to process
Yi Tseng51301292017-07-28 13:02:59 -0700670 * @return processed packet
671 */
rsahot036620655b2018-02-26 15:01:37 -0500672 private List<InternalPacket> processDhcpPacketFromClient(PacketContext context,
673 Ethernet ethernetPacket,
674 Set<Interface> clientInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -0700675 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
676 DeviceId receivedFromDevice = receivedFrom.deviceId();
rsahot036620655b2018-02-26 15:01:37 -0500677 Ip4Address relayAgentIp = null;
678 relayAgentIp = dhcp4HandlerUtil.getRelayAgentIPv4Address(clientInterfaces);
679 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
680 if (relayAgentIp == null || relayAgentMac == null) {
681 log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
682 + "packet from client on port: {}. Aborting packet processing",
683 clientInterfaces.iterator().next().connectPoint());
684 return null;
685 }
686 log.debug("Multi DHCP V4 processDhcpPacketFromClient on port {}",
687 clientInterfaces.iterator().next().connectPoint());
Yi Tseng25bfe372017-11-03 16:27:32 -0700688
Yi Tseng4f2a0462017-08-31 11:21:00 -0700689 // get dhcp header.
rsahot036620655b2018-02-26 15:01:37 -0500690 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Yi Tseng4f2a0462017-08-31 11:21:00 -0700691 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
692 UDP udpPacket = (UDP) ipv4Packet.getPayload();
693 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
694
Yi Tseng919b2df2017-09-07 16:22:51 -0700695
Yi Tsengdcef2c22017-08-05 20:34:06 -0700696 Ip4Address clientInterfaceIp =
697 interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
698 .stream()
699 .map(Interface::ipAddressesList)
700 .flatMap(Collection::stream)
701 .map(InterfaceIpAddress::ipAddress)
702 .filter(IpAddress::isIp4)
703 .map(IpAddress::getIp4Address)
704 .findFirst()
705 .orElse(null);
706 if (clientInterfaceIp == null) {
707 log.warn("Can't find interface IP for client interface for port {}",
rsahot036620655b2018-02-26 15:01:37 -0500708 context.inPacket().receivedFrom());
Yi Tsengdcef2c22017-08-05 20:34:06 -0700709 return null;
710 }
rsahot036620655b2018-02-26 15:01:37 -0500711
Yi Tseng4f2a0462017-08-31 11:21:00 -0700712 boolean isDirectlyConnected = directlyConnected(dhcpPacket);
rsahot036620655b2018-02-26 15:01:37 -0500713 boolean directConnFlag = directlyConnected(dhcpPacket);
714
715 // Multi DHCP Start
716 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
717 VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
718 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
719 .stream().filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
720 .findFirst()
721 .orElse(null);
722
723 List<InternalPacket> internalPackets = new ArrayList<>();
724 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
725 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
726
727
728 for (DhcpServerInfo serverInfo : copyServerInfoList) {
729 etherReply = (Ethernet) ethernetPacket.clone();
730 ipv4Packet = (IPv4) etherReply.getPayload();
731 udpPacket = (UDP) ipv4Packet.getPayload();
732 dhcpPacket = (DHCP) udpPacket.getPayload();
733 if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
734 log.warn("Can't get server connect point, ignore");
735 continue;
736 }
737 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
738 if (newServerInfo == null) {
739 log.warn("Can't get server interface with host info resolved, ignore");
740 continue;
741 }
742
743 Interface serverInterface = getServerInterface(newServerInfo);
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800744 if (serverInterface == null) {
rsahot036620655b2018-02-26 15:01:37 -0500745 log.warn("Can't get server interface, ignore");
746 continue;
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800747 }
Yi Tseng4f2a0462017-08-31 11:21:00 -0700748
rsahot036620655b2018-02-26 15:01:37 -0500749 Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
750 MacAddress macFacingServer = serverInterface.mac();
751 log.debug("Interfacing server {} Mac : {} ", ipFacingServer, macFacingServer);
752 if (ipFacingServer == null || macFacingServer == null) {
753 log.warn("No IP address for server Interface {}", serverInterface);
754 //return null;
755 continue;
756 }
Yi Tseng51301292017-07-28 13:02:59 -0700757
Charles Chand0d1e332017-10-10 16:53:32 -0400758
rsahot036620655b2018-02-26 15:01:37 -0500759 etherReply.setSourceMACAddress(macFacingServer);
760 // set default info and replace with indirect if available later on.
761 if (newServerInfo.getDhcpConnectMac().isPresent()) {
762 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
763 }
764 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
765 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
766 }
767 ipv4Packet.setSourceAddress(ipFacingServer.toInt());
768 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
Charles Chan2de55302018-03-02 18:27:59 -0800769 log.debug("Directly connected {}", isDirectlyConnected);
770 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().get());
rsahot036620655b2018-02-26 15:01:37 -0500771 if (isDirectlyConnected) {
Yi Tseng51301292017-07-28 13:02:59 -0700772
Charles Chan2de55302018-03-02 18:27:59 -0800773 log.debug("Default DHCP server IP: {}", newServerInfo.getDhcpServerIp4().get());
rsahot036620655b2018-02-26 15:01:37 -0500774 if (newServerInfo.getDhcpConnectMac().isPresent()) {
775 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
Charles Chand0d1e332017-10-10 16:53:32 -0400776 }
rsahot036620655b2018-02-26 15:01:37 -0500777 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
778 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
779 }
780
781 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
782
783
784 ConnectPoint inPort = context.inPacket().receivedFrom();
785 VlanId vlanId = VlanId.vlanId(ethernetPacket.getVlanID());
786 // add connected in port and vlan
787 CircuitId cid = new CircuitId(inPort.toString(), vlanId);
788 byte[] circuitId = cid.serialize();
789 DhcpOption circuitIdSubOpt = new DhcpOption();
790 circuitIdSubOpt
791 .setCode(CIRCUIT_ID.getValue())
792 .setLength((byte) circuitId.length)
793 .setData(circuitId);
794
795 DhcpRelayAgentOption newRelayAgentOpt = new DhcpRelayAgentOption();
796 newRelayAgentOpt.setCode(OptionCode_CircuitID.getValue());
797 newRelayAgentOpt.addSubOption(circuitIdSubOpt);
798
799 // Removes END option first
800 List<DhcpOption> options = dhcpPacket.getOptions().stream()
801 .filter(opt -> opt.getCode() != OptionCode_END.getValue())
802 .collect(Collectors.toList());
803
804 // push relay agent option
805 options.add(newRelayAgentOpt);
806
807 // make sure option 255(End) is the last option
808 DhcpOption endOption = new DhcpOption();
809 endOption.setCode(OptionCode_END.getValue());
810 options.add(endOption);
811
812 dhcpPacket.setOptions(options);
813
814 relayAgentIp = serverInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
815
816 // Sets relay agent IP
817 int effectiveRelayAgentIp = relayAgentIp != null ?
818 relayAgentIp.toInt() : clientInterfaceIp.toInt();
819 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
Charles Chan2de55302018-03-02 18:27:59 -0800820 log.debug("In Default, Relay Agent IP {}", effectiveRelayAgentIp);
Charles Chand0d1e332017-10-10 16:53:32 -0400821 } else {
rsahot036620655b2018-02-26 15:01:37 -0500822 if (!newServerInfo.getDhcpServerIp4().isPresent()) {
823 // do nothing
824 } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
825 continue;
826 } else {
827 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
828 // Sets relay agent IP
829 int effectiveRelayAgentIp = relayAgentIp != null ?
830 relayAgentIp.toInt() : clientInterfaceIp.toInt();
831 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
Charles Chand0d1e332017-10-10 16:53:32 -0400832 }
Yi Tseng4f2a0462017-08-31 11:21:00 -0700833 }
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700834
rsahot036620655b2018-02-26 15:01:37 -0500835 // Remove broadcast flag
836 dhcpPacket.setFlags((short) 0);
837
838 udpPacket.setPayload(dhcpPacket);
839 // As a DHCP relay, the source port should be server port( instead
840 // of client port.
841 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
842 udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
843 ipv4Packet.setPayload(udpPacket);
844 ipv4Packet.setTtl((byte) 64);
845 etherReply.setPayload(ipv4Packet);
846 InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
847 serverInfo.getDhcpServerConnectPoint().get());
848 internalPackets.add(internalPacket);
849 }
850 return internalPackets;
Yi Tseng51301292017-07-28 13:02:59 -0700851 }
852
Charles Chand0dd7002017-10-08 23:53:36 -0400853
854 /**
855 * Do a basic routing for a packet from client (used for LQ processing).
856 *
857 * @param context the packet context
858 * @param ethernetPacket the ethernet payload to process
859 * @return processed packet
860 */
rsahot036620655b2018-02-26 15:01:37 -0500861 private List<InternalPacket> processLeaseQueryFromAgent(PacketContext context,
862 Ethernet ethernetPacket) {
863 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
864 DeviceId receivedFromDevice = receivedFrom.deviceId();
865
Charles Chand0dd7002017-10-08 23:53:36 -0400866 // get dhcp header.
867 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
868 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
869 UDP udpPacket = (UDP) ipv4Packet.getPayload();
870 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
871
Charles Chan2de55302018-03-02 18:27:59 -0800872 Ip4Address relayAgentIp;
Charles Chand0dd7002017-10-08 23:53:36 -0400873
Charles Chand0dd7002017-10-08 23:53:36 -0400874 Ip4Address clientInterfaceIp =
875 interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
876 .stream()
877 .map(Interface::ipAddressesList)
878 .flatMap(Collection::stream)
879 .map(InterfaceIpAddress::ipAddress)
880 .filter(IpAddress::isIp4)
881 .map(IpAddress::getIp4Address)
882 .findFirst()
883 .orElse(null);
884 if (clientInterfaceIp == null) {
885 log.warn("Can't find interface IP for client interface for port {}",
rsahot036620655b2018-02-26 15:01:37 -0500886 context.inPacket().receivedFrom());
Charles Chand0dd7002017-10-08 23:53:36 -0400887 return null;
888 }
rsahot036620655b2018-02-26 15:01:37 -0500889
Charles Chand0dd7002017-10-08 23:53:36 -0400890 boolean isDirectlyConnected = directlyConnected(dhcpPacket);
rsahot036620655b2018-02-26 15:01:37 -0500891 boolean directConnFlag = directlyConnected(dhcpPacket);
892
893 // Multi DHCP Start
894 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
895 VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
rsahot036620655b2018-02-26 15:01:37 -0500896
897 List<InternalPacket> internalPackets = new ArrayList<>();
898 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
Charles Chan2de55302018-03-02 18:27:59 -0800899 List<DhcpServerInfo> copyServerInfoList = new ArrayList<>(serverInfoList);
rsahot036620655b2018-02-26 15:01:37 -0500900
901 for (DhcpServerInfo serverInfo : copyServerInfoList) {
902 // get dhcp header.
903 etherReply = (Ethernet) ethernetPacket.clone();
904 ipv4Packet = (IPv4) etherReply.getPayload();
905 udpPacket = (UDP) ipv4Packet.getPayload();
906 dhcpPacket = (DHCP) udpPacket.getPayload();
907
908 if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
909 log.warn("Can't get server connect point, ignore");
910 continue;
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800911 }
rsahot036620655b2018-02-26 15:01:37 -0500912 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
913 if (newServerInfo == null) {
914 log.warn("Can't get server interface with host info resolved, ignore");
915 continue;
916 }
Charles Chand0dd7002017-10-08 23:53:36 -0400917
rsahot036620655b2018-02-26 15:01:37 -0500918 Interface serverInterface = getServerInterface(newServerInfo);
919 if (serverInterface == null) {
920 log.warn("Can't get server interface, ignore");
921 continue;
922 }
923 Ip4Address ipFacingServer = getFirstIpFromInterface(serverInterface);
924 MacAddress macFacingServer = serverInterface.mac();
925 if (ipFacingServer == null || macFacingServer == null) {
926 log.warn("No IP address for server Interface {}", serverInterface);
927 continue;
928 }
Charles Chand0dd7002017-10-08 23:53:36 -0400929
rsahot036620655b2018-02-26 15:01:37 -0500930 etherReply.setSourceMACAddress(macFacingServer);
Charles Chan2de55302018-03-02 18:27:59 -0800931 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
932 etherReply.setVlanID(newServerInfo.getDhcpConnectVlan().get().toShort());
rsahot036620655b2018-02-26 15:01:37 -0500933 ipv4Packet.setSourceAddress(ipFacingServer.toInt());
Charles Chan2de55302018-03-02 18:27:59 -0800934 ipv4Packet.setDestinationAddress(newServerInfo.getDhcpServerIp4().get().toInt());
rsahot036620655b2018-02-26 15:01:37 -0500935 if (isDirectlyConnected) {
936 // set default info and replace with indirect if available later on.
937 if (newServerInfo.getDhcpConnectMac().isPresent()) {
938 etherReply.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
939 }
940 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
941 etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
942 }
943 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
944 // Sets relay agent IP
945 int effectiveRelayAgentIp = relayAgentIp != null ?
946 relayAgentIp.toInt() : clientInterfaceIp.toInt();
947 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
948 } else {
949 if (!newServerInfo.getDhcpServerIp4().isPresent()) {
950 //do nothing
951 } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
952 continue;
953 } else {
954 relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
955 // Sets relay agent IP
956 int effectiveRelayAgentIp = relayAgentIp != null ?
957 relayAgentIp.toInt() : clientInterfaceIp.toInt();
958 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
959 dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
960 log.info("Relay Agent IP {}", relayAgentIp);
961 }
962
963 log.info("In Direct");
964 }
965
966 // Remove broadcast flag
967 dhcpPacket.setFlags((short) 0);
968
969 udpPacket.setPayload(dhcpPacket);
970 // As a DHCP relay, the source port should be server port( instead
971 // of client port.
972 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
973 udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
974 ipv4Packet.setPayload(udpPacket);
975 ipv4Packet.setTtl((byte) 64);
976 etherReply.setPayload(ipv4Packet);
977 ////return etherReply;
978 Dhcp4HandlerUtil.InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
979 newServerInfo.getDhcpServerConnectPoint().get());
980 internalPackets.add(internalPacket);
Charles Chand0dd7002017-10-08 23:53:36 -0400981 }
rsahot036620655b2018-02-26 15:01:37 -0500982 log.warn("num of processLeaseQueryFromAgent packets to send is{}", internalPackets.size());
Charles Chand0dd7002017-10-08 23:53:36 -0400983
rsahot036620655b2018-02-26 15:01:37 -0500984 return internalPackets;
Charles Chand0dd7002017-10-08 23:53:36 -0400985 }
986
987
Yi Tseng51301292017-07-28 13:02:59 -0700988 /**
989 * Writes DHCP record to the store according to the request DHCP packet (Discover, Request).
990 *
991 * @param location the location which DHCP packet comes from
992 * @param ethernet the DHCP packet
993 * @param dhcpPayload the DHCP payload
994 */
995 private void writeRequestDhcpRecord(ConnectPoint location,
996 Ethernet ethernet,
997 DHCP dhcpPayload) {
998 VlanId vlanId = VlanId.vlanId(ethernet.getVlanID());
999 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1000 HostId hostId = HostId.hostId(macAddress, vlanId);
1001 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1002 if (record == null) {
1003 record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
1004 } else {
1005 record = record.clone();
1006 }
1007 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
1008 record.ip4Status(dhcpPayload.getPacketType());
1009 record.setDirectlyConnected(directlyConnected(dhcpPayload));
1010 if (!directlyConnected(dhcpPayload)) {
1011 // Update gateway mac address if the host is not directly connected
1012 record.nextHop(ethernet.getSourceMAC());
1013 }
1014 record.updateLastSeen();
1015 dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
1016 }
1017
1018 /**
1019 * Writes DHCP record to the store according to the response DHCP packet (Offer, Ack).
1020 *
1021 * @param ethernet the DHCP packet
1022 * @param dhcpPayload the DHCP payload
1023 */
1024 private void writeResponseDhcpRecord(Ethernet ethernet,
1025 DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001026 Optional<Interface> outInterface = getClientInterface(ethernet, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -07001027 if (!outInterface.isPresent()) {
1028 log.warn("Failed to determine where to send {}", dhcpPayload.getPacketType());
1029 return;
1030 }
1031
1032 Interface outIface = outInterface.get();
1033 ConnectPoint location = outIface.connectPoint();
Yi Tseng4f2a0462017-08-31 11:21:00 -07001034 VlanId vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001035 if (vlanId == null) {
1036 vlanId = outIface.vlan();
1037 }
Yi Tseng51301292017-07-28 13:02:59 -07001038 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1039 HostId hostId = HostId.hostId(macAddress, vlanId);
1040 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1041 if (record == null) {
1042 record = new DhcpRecord(HostId.hostId(macAddress, vlanId));
1043 } else {
1044 record = record.clone();
1045 }
1046 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
1047 if (dhcpPayload.getPacketType() == DHCP.MsgType.DHCPACK) {
1048 record.ip4Address(Ip4Address.valueOf(dhcpPayload.getYourIPAddress()));
1049 }
1050 record.ip4Status(dhcpPayload.getPacketType());
1051 record.setDirectlyConnected(directlyConnected(dhcpPayload));
1052 record.updateLastSeen();
1053 dhcpRelayStore.updateDhcpRecord(HostId.hostId(macAddress, vlanId), record);
1054 }
1055
1056 /**
1057 * Build the DHCP offer/ack with proper client port.
1058 *
1059 * @param ethernetPacket the original packet comes from server
1060 * @return new packet which will send to the client
1061 */
rsahot036620655b2018-02-26 15:01:37 -05001062 private Ethernet processDhcpPacketFromServer(PacketContext context, Ethernet ethernetPacket) {
Yi Tseng51301292017-07-28 13:02:59 -07001063 // get dhcp header.
rsahot036620655b2018-02-26 15:01:37 -05001064 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Yi Tseng51301292017-07-28 13:02:59 -07001065 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
1066 UDP udpPacket = (UDP) ipv4Packet.getPayload();
1067 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
1068
1069 // determine the vlanId of the client host - note that this vlan id
1070 // could be different from the vlan in the packet from the server
Yi Tsengdcef2c22017-08-05 20:34:06 -07001071 Interface clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
Yi Tseng51301292017-07-28 13:02:59 -07001072
Yi Tsengdcef2c22017-08-05 20:34:06 -07001073 if (clientInterface == null) {
Yi Tseng51301292017-07-28 13:02:59 -07001074 log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
1075 return null;
1076 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001077 VlanId vlanId;
rsahot036620655b2018-02-26 15:01:37 -05001078 ConnectPoint inPort = context.inPacket().receivedFrom();
1079 boolean directConnFlag = directlyConnected(dhcpPayload);
1080 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
1081
1082 if (foundServerInfo == null) {
1083 log.warn("Cannot find server info");
1084 return null;
1085 } else {
1086 if (dhcp4HandlerUtil.isServerIpEmpty(foundServerInfo)) {
1087 log.warn("Cannot find server info's ipaddress");
1088 return null;
1089 }
1090 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001091 if (clientInterface.vlanTagged().isEmpty()) {
1092 vlanId = clientInterface.vlan();
1093 } else {
1094 // might be multiple vlan in same interface
Yi Tseng4f2a0462017-08-31 11:21:00 -07001095 vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001096 }
1097 if (vlanId == null) {
1098 vlanId = VlanId.NONE;
1099 }
1100 etherReply.setVlanID(vlanId.toShort());
1101 etherReply.setSourceMACAddress(clientInterface.mac());
Yi Tseng51301292017-07-28 13:02:59 -07001102
Yi Tsengdcef2c22017-08-05 20:34:06 -07001103 if (!directlyConnected(dhcpPayload)) {
1104 // if client is indirectly connected, try use next hop mac address
1105 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1106 HostId hostId = HostId.hostId(macAddress, vlanId);
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001107 if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
1108 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1109 if (record != null) {
1110 // if next hop can be found, use mac address of next hop
1111 record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
1112 } else {
1113 // otherwise, discard the packet
1114 log.warn("Can't find record for host id {}, discard packet", hostId);
1115 return null;
1116 }
Yi Tsengdcef2c22017-08-05 20:34:06 -07001117 } else {
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001118 etherReply.setDestinationMACAddress(MacAddress.BROADCAST);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001119 }
Yi Tseng1696f562017-08-17 17:43:38 -07001120 } else {
1121 etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
Yi Tsengdcef2c22017-08-05 20:34:06 -07001122 }
1123
Yi Tseng51301292017-07-28 13:02:59 -07001124 // we leave the srcMac from the original packet
Yi Tseng51301292017-07-28 13:02:59 -07001125 // figure out the relay agent IP corresponding to the original request
Yi Tseng3df7f9d2017-08-17 13:08:31 -07001126 Ip4Address ipFacingClient = getFirstIpFromInterface(clientInterface);
1127 if (ipFacingClient == null) {
Yi Tseng51301292017-07-28 13:02:59 -07001128 log.warn("Cannot determine relay agent interface Ipv4 addr for host {}/{}. "
1129 + "Aborting relay for dhcp packet from server {}",
Yi Tsengdcef2c22017-08-05 20:34:06 -07001130 etherReply.getDestinationMAC(), clientInterface.vlan(),
Yi Tseng51301292017-07-28 13:02:59 -07001131 ethernetPacket);
1132 return null;
1133 }
1134 // SRC_IP: relay agent IP
1135 // DST_IP: offered IP
Yi Tseng3df7f9d2017-08-17 13:08:31 -07001136 ipv4Packet.setSourceAddress(ipFacingClient.toInt());
Daniel Ginsburgb1dec912018-01-29 11:40:22 -08001137 if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
1138 ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
1139 } else {
1140 ipv4Packet.setDestinationAddress(BROADCAST_IP);
1141 }
Yi Tseng51301292017-07-28 13:02:59 -07001142 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
1143 if (directlyConnected(dhcpPayload)) {
1144 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
1145 } else {
1146 // forward to another dhcp relay
Yi Tseng93ba53c2017-09-14 13:24:21 -07001147 // FIXME: Currently we assume the DHCP comes from a L2 relay with
1148 // Option 82, this might not work if DHCP message comes from
1149 // L3 relay.
1150 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
Yi Tseng51301292017-07-28 13:02:59 -07001151 }
1152
1153 udpPacket.setPayload(dhcpPayload);
1154 ipv4Packet.setPayload(udpPacket);
1155 etherReply.setPayload(ipv4Packet);
1156 return etherReply;
1157 }
1158
Yi Tsengdcef2c22017-08-05 20:34:06 -07001159 /**
Charles Chand0dd7002017-10-08 23:53:36 -04001160 * Build the DHCP offer/ack with proper client port.
1161 *
1162 * @param ethernetPacket the original packet comes from server
1163 * @return new packet which will send to the client
1164 */
1165 private Ethernet processLeaseQueryFromServer(Ethernet ethernetPacket) {
1166 // get dhcp header.
1167 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
1168 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
1169 UDP udpPacket = (UDP) ipv4Packet.getPayload();
1170 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
1171
1172 // determine the vlanId of the client host - note that this vlan id
1173 // could be different from the vlan in the packet from the server
1174 Interface clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
1175
1176 if (clientInterface == null) {
1177 log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
1178 return null;
1179 }
1180 VlanId vlanId;
1181 if (clientInterface.vlanTagged().isEmpty()) {
1182 vlanId = clientInterface.vlan();
1183 } else {
1184 // might be multiple vlan in same interface
1185 vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
1186 }
1187 if (vlanId == null) {
1188 vlanId = VlanId.NONE;
1189 }
1190 etherReply.setVlanID(vlanId.toShort());
1191 etherReply.setSourceMACAddress(clientInterface.mac());
1192
1193 if (!directlyConnected(dhcpPayload)) {
1194 // if client is indirectly connected, try use next hop mac address
1195 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
1196 HostId hostId = HostId.hostId(macAddress, vlanId);
1197 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1198 if (record != null) {
1199 // if next hop can be found, use mac address of next hop
1200 record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
1201 } else {
1202 // otherwise, discard the packet
1203 log.warn("Can't find record for host id {}, discard packet", hostId);
1204 return null;
1205 }
1206 } else {
1207 etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
1208 }
1209
1210 // default is client port
1211 udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
1212 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
1213
1214 udpPacket.setPayload(dhcpPayload);
1215 ipv4Packet.setPayload(udpPacket);
1216 etherReply.setPayload(ipv4Packet);
1217 return etherReply;
1218 }
1219 /**
Yi Tsengdcef2c22017-08-05 20:34:06 -07001220 * Extracts VLAN ID from relay agent option.
1221 *
1222 * @param dhcpPayload the DHCP payload
1223 * @return VLAN ID from DHCP payload; null if not exists
1224 */
Yi Tseng4f2a0462017-08-31 11:21:00 -07001225 private VlanId getVlanIdFromRelayAgentOption(DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001226 DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
1227 if (option == null) {
1228 return null;
1229 }
1230 DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
1231 if (circuitIdSubOption == null) {
1232 return null;
1233 }
1234 try {
1235 CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
1236 return circuitId.vlanId();
1237 } catch (IllegalArgumentException e) {
1238 // can't deserialize the circuit ID
1239 return null;
1240 }
1241 }
1242
1243 /**
1244 * Removes DHCP relay agent information option (option 82) from DHCP payload.
1245 * Also reset giaddr to 0
1246 *
1247 * @param ethPacket the Ethernet packet to be processed
1248 * @return Ethernet packet processed
1249 */
1250 private Ethernet removeRelayAgentOption(Ethernet ethPacket) {
rsahot036620655b2018-02-26 15:01:37 -05001251 Ethernet ethernet = (Ethernet) ethPacket.duplicate();
Yi Tsengdcef2c22017-08-05 20:34:06 -07001252 IPv4 ipv4 = (IPv4) ethernet.getPayload();
1253 UDP udp = (UDP) ipv4.getPayload();
1254 DHCP dhcpPayload = (DHCP) udp.getPayload();
1255
1256 // removes relay agent information option
1257 List<DhcpOption> options = dhcpPayload.getOptions();
1258 options = options.stream()
1259 .filter(option -> option.getCode() != OptionCode_CircuitID.getValue())
1260 .collect(Collectors.toList());
1261 dhcpPayload.setOptions(options);
1262 dhcpPayload.setGatewayIPAddress(0);
1263
1264 udp.setPayload(dhcpPayload);
1265 ipv4.setPayload(udp);
1266 ethernet.setPayload(ipv4);
1267 return ethernet;
1268 }
1269
Yi Tseng51301292017-07-28 13:02:59 -07001270
1271 /**
1272 * Check if the host is directly connected to the network or not.
1273 *
1274 * @param dhcpPayload the dhcp payload
1275 * @return true if the host is directly connected to the network; false otherwise
1276 */
1277 private boolean directlyConnected(DHCP dhcpPayload) {
Yi Tseng440e2b72017-08-24 14:47:34 -07001278 DhcpRelayAgentOption relayAgentOption =
1279 (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
Yi Tseng51301292017-07-28 13:02:59 -07001280
1281 // Doesn't contains relay option
1282 if (relayAgentOption == null) {
1283 return true;
1284 }
1285
Yi Tseng440e2b72017-08-24 14:47:34 -07001286 // check circuit id, if circuit id is invalid, we say it is an indirect host
1287 DhcpOption circuitIdOpt = relayAgentOption.getSubOption(CIRCUIT_ID.getValue());
Yi Tseng51301292017-07-28 13:02:59 -07001288
Yi Tseng440e2b72017-08-24 14:47:34 -07001289 try {
1290 CircuitId.deserialize(circuitIdOpt.getData());
Yi Tseng51301292017-07-28 13:02:59 -07001291 return true;
Yi Tseng440e2b72017-08-24 14:47:34 -07001292 } catch (Exception e) {
1293 // invalid circuit id
1294 return false;
Yi Tseng51301292017-07-28 13:02:59 -07001295 }
Yi Tseng51301292017-07-28 13:02:59 -07001296 }
1297
1298
1299 /**
1300 * Send the DHCP ack to the requester host.
1301 * Modify Host or Route store according to the type of DHCP.
1302 *
1303 * @param ethernetPacketAck the packet
1304 * @param dhcpPayload the DHCP data
1305 */
1306 private void handleDhcpAck(Ethernet ethernetPacketAck, DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001307 Optional<Interface> outInterface = getClientInterface(ethernetPacketAck, dhcpPayload);
Yi Tseng51301292017-07-28 13:02:59 -07001308 if (!outInterface.isPresent()) {
1309 log.warn("Can't find output interface for dhcp: {}", dhcpPayload);
1310 return;
1311 }
1312
1313 Interface outIface = outInterface.get();
1314 HostLocation hostLocation = new HostLocation(outIface.connectPoint(), System.currentTimeMillis());
1315 MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
Yi Tseng4f2a0462017-08-31 11:21:00 -07001316 VlanId vlanId = getVlanIdFromRelayAgentOption(dhcpPayload);
Yi Tsengdcef2c22017-08-05 20:34:06 -07001317 if (vlanId == null) {
1318 vlanId = outIface.vlan();
1319 }
Yi Tseng51301292017-07-28 13:02:59 -07001320 HostId hostId = HostId.hostId(macAddress, vlanId);
1321 Ip4Address ip = Ip4Address.valueOf(dhcpPayload.getYourIPAddress());
1322
1323 if (directlyConnected(dhcpPayload)) {
1324 // Add to host store if it connect to network directly
1325 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tsengaa417a62017-09-08 17:22:51 -07001326 Host host = hostService.getHost(hostId);
Yi Tseng51301292017-07-28 13:02:59 -07001327
Yi Tsengaa417a62017-09-08 17:22:51 -07001328 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
1329 if (host != null) {
1330 // Dual homing support:
1331 // if host exists, use old locations and new location
1332 hostLocations.addAll(host.locations());
1333 }
1334 HostDescription desc = new DefaultHostDescription(macAddress, vlanId,
1335 hostLocations, ips, false);
1336 // Add IP address when dhcp server give the host new ip address
1337 providerService.hostDetected(hostId, desc, false);
Yi Tseng51301292017-07-28 13:02:59 -07001338 } else {
1339 // Add to route store if it does not connect to network directly
1340 // Get gateway host IP according to host mac address
Yi Tsengdcef2c22017-08-05 20:34:06 -07001341 // TODO: remove relay store here
Yi Tseng51301292017-07-28 13:02:59 -07001342 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
1343
1344 if (record == null) {
1345 log.warn("Can't find DHCP record of host {}", hostId);
1346 return;
1347 }
1348
1349 MacAddress gwMac = record.nextHop().orElse(null);
1350 if (gwMac == null) {
1351 log.warn("Can't find gateway mac address from record {}", record);
1352 return;
1353 }
1354
1355 HostId gwHostId = HostId.hostId(gwMac, record.vlanId());
1356 Host gwHost = hostService.getHost(gwHostId);
1357
1358 if (gwHost == null) {
1359 log.warn("Can't find gateway host {}", gwHostId);
1360 return;
1361 }
1362
1363 Ip4Address nextHopIp = gwHost.ipAddresses()
1364 .stream()
1365 .filter(IpAddress::isIp4)
1366 .map(IpAddress::getIp4Address)
1367 .findFirst()
1368 .orElse(null);
1369
1370 if (nextHopIp == null) {
1371 log.warn("Can't find IP address of gateway {}", gwHost);
1372 return;
1373 }
1374
1375 Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
1376 routeStore.updateRoute(route);
1377 }
Yi Tseng51301292017-07-28 13:02:59 -07001378 }
1379
1380 /**
Yi Tseng51301292017-07-28 13:02:59 -07001381 * Gets output interface of a dhcp packet.
1382 * If option 82 exists in the dhcp packet and the option was sent by
Yi Tseng4f2a0462017-08-31 11:21:00 -07001383 * ONOS (circuit format is correct), use the connect
Yi Tseng51301292017-07-28 13:02:59 -07001384 * point and vlan id from circuit id; otherwise, find host by destination
1385 * address and use vlan id from sender (dhcp server).
1386 *
1387 * @param ethPacket the ethernet packet
1388 * @param dhcpPayload the dhcp packet
1389 * @return an interface represent the output port and vlan; empty value
1390 * if the host or circuit id not found
1391 */
Yi Tsengdcef2c22017-08-05 20:34:06 -07001392 private Optional<Interface> getClientInterface(Ethernet ethPacket, DHCP dhcpPayload) {
Yi Tseng51301292017-07-28 13:02:59 -07001393 VlanId originalPacketVlanId = VlanId.vlanId(ethPacket.getVlanID());
Yi Tseng51301292017-07-28 13:02:59 -07001394 DhcpRelayAgentOption option = (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
1395
Yi Tseng4f2a0462017-08-31 11:21:00 -07001396 DhcpOption circuitIdSubOption = option.getSubOption(CIRCUIT_ID.getValue());
1397 try {
1398 CircuitId circuitId = CircuitId.deserialize(circuitIdSubOption.getData());
1399 ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(circuitId.connectPoint());
1400 VlanId vlanId = circuitId.vlanId();
1401 return interfaceService.getInterfacesByPort(connectPoint)
1402 .stream()
1403 .filter(iface -> interfaceContainsVlan(iface, vlanId))
1404 .findFirst();
1405 } catch (IllegalArgumentException ex) {
1406 // invalid circuit format, didn't sent by ONOS
1407 log.debug("Invalid circuit {}, use information from dhcp payload",
1408 circuitIdSubOption.getData());
Yi Tseng51301292017-07-28 13:02:59 -07001409 }
1410
1411 // Use Vlan Id from DHCP server if DHCP relay circuit id was not
1412 // sent by ONOS or circuit Id can't be parsed
Yi Tsengdcef2c22017-08-05 20:34:06 -07001413 // TODO: remove relay store from this method
Yi Tseng51301292017-07-28 13:02:59 -07001414 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
1415 Optional<DhcpRecord> dhcpRecord = dhcpRelayStore.getDhcpRecord(HostId.hostId(dstMac, originalPacketVlanId));
Yi Tsengdcef2c22017-08-05 20:34:06 -07001416 ConnectPoint clientConnectPoint = dhcpRecord
Yi Tseng51301292017-07-28 13:02:59 -07001417 .map(DhcpRecord::locations)
1418 .orElse(Collections.emptySet())
1419 .stream()
1420 .reduce((hl1, hl2) -> {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001421 // find latest host connect point
Yi Tseng51301292017-07-28 13:02:59 -07001422 if (hl1 == null || hl2 == null) {
1423 return hl1 == null ? hl2 : hl1;
1424 }
1425 return hl1.time() > hl2.time() ? hl1 : hl2;
1426 })
Yi Tsengdcef2c22017-08-05 20:34:06 -07001427 .orElse(null);
Yi Tseng51301292017-07-28 13:02:59 -07001428
Yi Tsengdcef2c22017-08-05 20:34:06 -07001429 if (clientConnectPoint != null) {
1430 return interfaceService.getInterfacesByPort(clientConnectPoint)
1431 .stream()
Yi Tseng4f2a0462017-08-31 11:21:00 -07001432 .filter(iface -> interfaceContainsVlan(iface, originalPacketVlanId))
Yi Tsengdcef2c22017-08-05 20:34:06 -07001433 .findFirst();
1434 }
1435 return Optional.empty();
Yi Tseng51301292017-07-28 13:02:59 -07001436 }
1437
1438 /**
1439 * Send the response DHCP to the requester host.
1440 *
1441 * @param ethPacket the packet
1442 * @param dhcpPayload the DHCP data
1443 */
1444 private void sendResponseToClient(Ethernet ethPacket, DHCP dhcpPayload) {
Yi Tsengdcef2c22017-08-05 20:34:06 -07001445 Optional<Interface> outInterface = getClientInterface(ethPacket, dhcpPayload);
1446 if (directlyConnected(dhcpPayload)) {
1447 ethPacket = removeRelayAgentOption(ethPacket);
1448 }
1449 if (!outInterface.isPresent()) {
1450 log.warn("Can't find output interface for client, ignore");
1451 return;
1452 }
1453 Interface outIface = outInterface.get();
1454 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1455 .setOutput(outIface.connectPoint().port())
1456 .build();
1457 OutboundPacket o = new DefaultOutboundPacket(
1458 outIface.connectPoint().deviceId(),
1459 treatment,
1460 ByteBuffer.wrap(ethPacket.serialize()));
1461 if (log.isTraceEnabled()) {
1462 log.trace("Relaying packet to DHCP client {} via {}, vlan {}",
1463 ethPacket,
1464 outIface.connectPoint(),
1465 outIface.vlan());
1466 }
1467 packetService.emit(o);
Yi Tseng51301292017-07-28 13:02:59 -07001468 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001469
Yi Tsengaa417a62017-09-08 17:22:51 -07001470 @Override
1471 public void triggerProbe(Host host) {
1472 // Do nothing here
1473 }
1474
1475 @Override
1476 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -07001477 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -07001478 }
1479
Yi Tseng483ac6f2017-08-02 15:03:31 -07001480 class InternalHostListener implements HostListener {
1481 @Override
1482 public void event(HostEvent event) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001483 if (!configured()) {
1484 return;
1485 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001486 switch (event.type()) {
1487 case HOST_ADDED:
1488 case HOST_UPDATED:
1489 hostUpdated(event.subject());
1490 break;
1491 case HOST_REMOVED:
1492 hostRemoved(event.subject());
1493 break;
Yi Tseng483ac6f2017-08-02 15:03:31 -07001494 default:
1495 break;
1496 }
1497 }
1498 }
1499
1500 /**
Yi Tseng483ac6f2017-08-02 15:03:31 -07001501 * Handle host updated.
1502 * If the host is DHCP server or gateway, update connect mac and vlan.
1503 *
1504 * @param host the host
1505 */
1506 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001507 hostUpdated(host, defaultServerInfoList);
1508 hostUpdated(host, indirectServerInfoList);
Yi Tseng483ac6f2017-08-02 15:03:31 -07001509 }
1510
Yi Tseng525ff402017-10-23 19:39:39 -07001511 private void hostUpdated(Host host, List<DhcpServerInfo> srverInfoList) {
1512 DhcpServerInfo serverInfo;
1513 Ip4Address targetIp;
1514 if (!srverInfoList.isEmpty()) {
1515 serverInfo = srverInfoList.get(0);
1516 targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
1517 Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
1518
1519 if (targetIp == null) {
1520 targetIp = serverIp;
1521 }
1522
1523 if (targetIp != null) {
1524 if (host.ipAddresses().contains(targetIp)) {
1525 serverInfo.setDhcpConnectMac(host.mac());
1526 serverInfo.setDhcpConnectVlan(host.vlan());
1527 requestDhcpPacket(serverIp);
1528 }
1529 }
1530 }
1531 }
1532
1533
Yi Tseng483ac6f2017-08-02 15:03:31 -07001534 /**
1535 * Handle host removed.
1536 * If the host is DHCP server or gateway, unset connect mac and vlan.
1537 *
1538 * @param host the host
1539 */
1540 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001541 hostRemoved(host, defaultServerInfoList);
1542 hostRemoved(host, indirectServerInfoList);
1543 }
1544
1545 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001546 DhcpServerInfo serverInfo;
Yi Tseng525ff402017-10-23 19:39:39 -07001547 Ip4Address targetIp;
1548 if (!serverInfoList.isEmpty()) {
1549 serverInfo = serverInfoList.get(0);
1550 Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
1551 targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
Yi Tseng919b2df2017-09-07 16:22:51 -07001552
Yi Tseng525ff402017-10-23 19:39:39 -07001553 if (targetIp == null) {
1554 targetIp = serverIp;
Yi Tseng919b2df2017-09-07 16:22:51 -07001555 }
Yi Tseng525ff402017-10-23 19:39:39 -07001556
1557 if (targetIp != null) {
1558 if (host.ipAddresses().contains(targetIp)) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001559 serverInfo.setDhcpConnectVlan(null);
1560 serverInfo.setDhcpConnectMac(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001561 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001562 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001563 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001564 }
Yi Tseng525ff402017-10-23 19:39:39 -07001565 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001566
Yi Tseng525ff402017-10-23 19:39:39 -07001567 private void requestDhcpPacket(Ip4Address serverIp) {
1568 requestServerDhcpPacket(serverIp);
1569 requestClientDhcpPacket(serverIp);
1570 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001571
Yi Tseng525ff402017-10-23 19:39:39 -07001572 private void cancelDhcpPacket(Ip4Address serverIp) {
1573 cancelServerDhcpPacket(serverIp);
1574 cancelClientDhcpPacket(serverIp);
1575 }
1576
1577 private void cancelServerDhcpPacket(Ip4Address serverIp) {
1578 TrafficSelector serverSelector =
1579 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1580 .matchIPSrc(serverIp.toIpPrefix())
1581 .build();
1582 packetService.cancelPackets(serverSelector,
1583 PacketPriority.CONTROL,
1584 appId);
1585 }
1586
1587 private void requestServerDhcpPacket(Ip4Address serverIp) {
1588 TrafficSelector serverSelector =
1589 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1590 .matchIPSrc(serverIp.toIpPrefix())
1591 .build();
1592 packetService.requestPackets(serverSelector,
1593 PacketPriority.CONTROL,
1594 appId);
1595 }
1596
1597 private void cancelClientDhcpPacket(Ip4Address serverIp) {
1598 // Packet comes from relay
1599 TrafficSelector indirectClientSelector =
1600 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1601 .matchIPDst(serverIp.toIpPrefix())
1602 .build();
1603 packetService.cancelPackets(indirectClientSelector,
1604 PacketPriority.CONTROL,
1605 appId);
1606
1607 // Packet comes from client
1608 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1609 PacketPriority.CONTROL,
1610 appId);
1611 }
1612
1613 private void requestClientDhcpPacket(Ip4Address serverIp) {
1614 // Packet comes from relay
1615 TrafficSelector indirectClientSelector =
1616 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1617 .matchIPDst(serverIp.toIpPrefix())
1618 .build();
1619 packetService.requestPackets(indirectClientSelector,
1620 PacketPriority.CONTROL,
1621 appId);
1622
1623 // Packet comes from client
1624 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1625 PacketPriority.CONTROL,
1626 appId);
1627 }
1628
1629 /**
1630 * Process the ignore rules.
1631 *
1632 * @param deviceId the device id
1633 * @param vlanId the vlan to be ignored
1634 * @param op the operation, ADD to install; REMOVE to uninstall rules
1635 */
1636 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001637 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1638 DHCP_SELECTORS.forEach(trafficSelector -> {
1639 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1640 .matchVlanId(vlanId)
1641 .build();
1642
1643 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1644 .withFlag(ForwardingObjective.Flag.VERSATILE)
1645 .withSelector(selector)
1646 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001647 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001648 .fromApp(appId);
1649
1650
1651 ObjectiveContext objectiveContext = new ObjectiveContext() {
1652 @Override
1653 public void onSuccess(Objective objective) {
1654 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1655 op, vlanId, deviceId, selector);
1656 int countDown = installedCount.decrementAndGet();
1657 if (countDown != 0) {
1658 return;
1659 }
1660 switch (op) {
1661 case ADD:
1662 ignoredVlans.put(deviceId, vlanId);
1663 break;
1664 case REMOVE:
1665 ignoredVlans.remove(deviceId, vlanId);
1666 break;
1667 default:
1668 log.warn("Unsupported objective operation {}", op);
1669 break;
1670 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001671 }
Yi Tseng525ff402017-10-23 19:39:39 -07001672
1673 @Override
1674 public void onError(Objective objective, ObjectiveError error) {
1675 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1676 op, vlanId, selector, deviceId, error);
Yi Tseng919b2df2017-09-07 16:22:51 -07001677 }
Yi Tseng525ff402017-10-23 19:39:39 -07001678 };
1679
1680 ForwardingObjective fwd;
1681 switch (op) {
1682 case ADD:
1683 fwd = builder.add(objectiveContext);
1684 break;
1685 case REMOVE:
1686 fwd = builder.remove(objectiveContext);
1687 break;
1688 default:
1689 log.warn("Unsupported objective operation {}", op);
1690 return;
Yi Tseng4f2a0462017-08-31 11:21:00 -07001691 }
Yi Tseng525ff402017-10-23 19:39:39 -07001692
1693 Device device = deviceService.getDevice(deviceId);
1694 if (device == null || !device.is(Pipeliner.class)) {
1695 log.warn("Device {} is not available now, wait until device is available", deviceId);
1696 return;
1697 }
1698 flowObjectiveService.apply(deviceId, fwd);
1699 });
Yi Tseng483ac6f2017-08-02 15:03:31 -07001700 }
Kalhee Kimba366062017-11-07 16:32:09 +00001701
1702 @Override
1703 public void setDhcpFpmEnabled(Boolean enabled) {
1704 // v4 does not use fpm. Do nothing.
1705 }
rsahot036620655b2018-02-26 15:01:37 -05001706 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1707 List<DhcpServerInfo> validServerInfo;
1708
1709 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1710 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1711 } else {
1712 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1713 }
1714 return validServerInfo;
1715 }
1716
1717
1718 private boolean checkDhcpServerConnPt(boolean directConnFlag,
1719 DhcpServerInfo serverInfo) {
1720 if (serverInfo.getDhcpServerConnectPoint() == null) {
1721 log.warn("DHCP4 server connect point for {} connPt {}",
1722 directConnFlag ? "direct" : "indirect", serverInfo.getDhcpServerConnectPoint());
1723 return false;
1724 }
1725 return true;
1726 }
1727
1728 /**
1729 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1730 *
1731 * @param serverInfo server information
1732 * @return newServerInfo if host info can be either found or filled in.
1733 */
1734 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1735 DhcpServerInfo newServerInfo = null;
1736 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1737 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1738 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1739
1740 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1741 newServerInfo = serverInfo;
Charles Chan2de55302018-03-02 18:27:59 -08001742 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp4(),
rsahot036620655b2018-02-26 15:01:37 -05001743 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1744 } else {
1745 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp4(),
1746 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1747
1748 Ip4Address ipToProbe;
1749 if (serverInfo.getDhcpGatewayIp4().isPresent()) {
1750 ipToProbe = serverInfo.getDhcpGatewayIp4().get();
1751 } else {
1752 ipToProbe = serverInfo.getDhcpServerIp4().orElse(null);
1753 }
1754 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1755 .map(ip -> "gateway").orElse("server");
1756
1757 log.warn("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1758 hostService.startMonitoringIp(ipToProbe);
1759
1760 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1761 if (!hosts.isEmpty()) {
1762 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1763 Host host = hosts.iterator().next();
1764 serverInfo.setDhcpConnectVlan(host.vlan());
1765 serverInfo.setDhcpConnectMac(host.mac());
1766 // replace the serverInfo in the list
1767 sererInfoList.set(serverInfoIndex, serverInfo);
1768 newServerInfo = serverInfo;
1769 log.warn("Dynamically host found host {}", host);
1770 } else {
1771 log.warn("No host found host ip {} dynamically", ipToProbe);
1772 }
1773 }
1774 return newServerInfo;
1775 }
1776
1777 /**
1778 * Gets Interface facing to the server for default host.
1779 *
1780 * @param serverInfo server information
1781 * @return the Interface facing to the server; null if not found
1782 */
1783 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1784 Interface serverInterface = null;
1785
1786 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1787 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1788
1789 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1790 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1791 .stream()
1792 .filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1793 .findFirst()
1794 .orElse(null);
1795 } else {
1796 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1797 dhcpServerConnectPoint, dhcpConnectVlan);
1798 }
1799
1800 return serverInterface;
1801 }
1802
1803 //forward the packet to ConnectPoint where the DHCP server is attached.
1804 private void forwardPacket(InternalPacket packet) {
1805 //send Packetout to dhcp server connectpoint.
1806 if (packet.destLocation != null) {
1807 TrafficTreatment t = DefaultTrafficTreatment.builder()
1808 .setOutput(packet.destLocation.port()).build();
1809 OutboundPacket o = new DefaultOutboundPacket(
1810 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
1811 if (log.isTraceEnabled()) {
1812 log.trace("Relaying packet to destination {}", packet.destLocation);
1813 }
Charles Chan2de55302018-03-02 18:27:59 -08001814 log.debug("packetService.emit(o) to port {}", packet.destLocation);
rsahot036620655b2018-02-26 15:01:37 -05001815 packetService.emit(o);
1816 }
1817 }
1818
1819
1820 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1821 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1822 DhcpServerInfo foundServerInfo = null;
1823 for (DhcpServerInfo serverInfo : validServerInfoList) {
1824 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1825 foundServerInfo = serverInfo;
Charles Chan2de55302018-03-02 18:27:59 -08001826 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
rsahot036620655b2018-02-26 15:01:37 -05001827 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1828 break;
1829 } else {
1830 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1831 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1832 }
1833 }
1834 return foundServerInfo;
1835 }
1836
Yi Tseng51301292017-07-28 13:02:59 -07001837}