blob: c7e49133314360ce53ad9bd5f1091c15438a63f5 [file] [log] [blame]
Amit Ghosh47243cb2017-07-26 05:08:53 +01001/*
Deepa vaddireddy0060f532017-08-04 06:46:05 +00002 * Copyright 2017-present Open Networking Foundation
Amit Ghosh47243cb2017-07-26 05:08:53 +01003 *
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 */
Matteo Scandolo57af5d12019-04-29 17:11:41 -070016package org.opencord.dhcpl2relay.impl;
Amit Ghosh47243cb2017-07-26 05:08:53 +010017
Carmelo Casconede1e6e32019-07-15 19:39:08 -070018import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
Amit Ghosh47243cb2017-07-26 05:08:53 +010022import org.onlab.packet.DHCP;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000023import org.onlab.packet.Ethernet;
Amit Ghosh47243cb2017-07-26 05:08:53 +010024import org.onlab.packet.IPv4;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053025import org.onlab.packet.IpAddress;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000026import org.onlab.packet.MacAddress;
Amit Ghosh47243cb2017-07-26 05:08:53 +010027import org.onlab.packet.TpPort;
28import org.onlab.packet.UDP;
29import org.onlab.packet.VlanId;
Jonathan Hartedbf6422018-05-02 17:30:05 -070030import org.onlab.packet.dhcp.DhcpOption;
Amit Ghosh47243cb2017-07-26 05:08:53 +010031import org.onlab.util.Tools;
32import org.onosproject.cfg.ComponentConfigService;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
Jonathan Hartc36c9552018-07-31 15:07:53 -040035import org.onosproject.event.AbstractListenerManager;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053036import org.onosproject.mastership.MastershipEvent;
37import org.onosproject.mastership.MastershipListener;
38import org.onosproject.mastership.MastershipService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010039import org.onosproject.net.AnnotationKeys;
40import org.onosproject.net.ConnectPoint;
Amit Ghosh83c8c892017-11-09 11:08:27 +000041import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
Amit Ghosh47243cb2017-07-26 05:08:53 +010043import org.onosproject.net.Host;
44import org.onosproject.net.Port;
Amit Ghosh83c8c892017-11-09 11:08:27 +000045import org.onosproject.net.PortNumber;
Amit Ghosh47243cb2017-07-26 05:08:53 +010046import org.onosproject.net.config.ConfigFactory;
47import org.onosproject.net.config.NetworkConfigEvent;
48import org.onosproject.net.config.NetworkConfigListener;
49import org.onosproject.net.config.NetworkConfigRegistry;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053050import org.onosproject.net.device.DeviceEvent;
51import org.onosproject.net.device.DeviceListener;
Amit Ghosh47243cb2017-07-26 05:08:53 +010052import org.onosproject.net.device.DeviceService;
53import org.onosproject.net.flow.DefaultTrafficSelector;
54import org.onosproject.net.flow.DefaultTrafficTreatment;
55import org.onosproject.net.flow.TrafficSelector;
56import org.onosproject.net.flow.TrafficTreatment;
Saurav Dasb4e3e102018-10-02 15:31:17 -070057import org.onosproject.net.flowobjective.FlowObjectiveService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010058import org.onosproject.net.host.HostService;
59import org.onosproject.net.packet.DefaultOutboundPacket;
60import org.onosproject.net.packet.OutboundPacket;
61import org.onosproject.net.packet.PacketContext;
62import org.onosproject.net.packet.PacketPriority;
63import org.onosproject.net.packet.PacketProcessor;
64import org.onosproject.net.packet.PacketService;
Matteo Scandolo57af5d12019-04-29 17:11:41 -070065import org.opencord.dhcpl2relay.DhcpAllocationInfo;
66import org.opencord.dhcpl2relay.DhcpL2RelayEvent;
67import org.opencord.dhcpl2relay.DhcpL2RelayListener;
68import org.opencord.dhcpl2relay.DhcpL2RelayService;
69import org.opencord.dhcpl2relay.impl.packet.DhcpOption82;
Gamze Abakac806c6c2018-12-03 12:49:46 +000070import org.opencord.sadis.BaseInformationService;
71import org.opencord.sadis.SadisService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010072import org.opencord.sadis.SubscriberAndDeviceInformation;
Amit Ghosh47243cb2017-07-26 05:08:53 +010073import org.osgi.service.component.ComponentContext;
Carmelo Casconede1e6e32019-07-15 19:39:08 -070074import org.osgi.service.component.annotations.Activate;
75import org.osgi.service.component.annotations.Component;
76import org.osgi.service.component.annotations.Deactivate;
77import org.osgi.service.component.annotations.Modified;
78import org.osgi.service.component.annotations.Reference;
79import org.osgi.service.component.annotations.ReferenceCardinality;
Amit Ghosh47243cb2017-07-26 05:08:53 +010080import org.slf4j.Logger;
81import org.slf4j.LoggerFactory;
82
Carmelo Casconede1e6e32019-07-15 19:39:08 -070083import java.nio.ByteBuffer;
84import java.util.ArrayList;
85import java.util.Arrays;
86import java.util.Dictionary;
87import java.util.List;
88import java.util.Map;
89import java.util.Optional;
90import java.util.Set;
91import java.util.concurrent.atomic.AtomicReference;
92import java.util.stream.Collectors;
93
94import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
95import static org.onlab.packet.MacAddress.valueOf;
96import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
97import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.ENABLE_DHCP_BROADCAST_REPLIES;
98import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.ENABLE_DHCP_BROADCAST_REPLIES_DEFAULT;
99import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.OPTION_82;
100import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.OPTION_82_DEFAULT;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100101
102/**
103 * DHCP Relay Agent Application Component.
104 */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700105@Component(immediate = true,
106property = {
107 OPTION_82 + ":Boolean=" + OPTION_82_DEFAULT,
108 ENABLE_DHCP_BROADCAST_REPLIES + ":Boolean=" + ENABLE_DHCP_BROADCAST_REPLIES_DEFAULT,
109})
Jonathan Hartc36c9552018-07-31 15:07:53 -0400110public class DhcpL2Relay
111 extends AbstractListenerManager<DhcpL2RelayEvent, DhcpL2RelayListener>
112 implements DhcpL2RelayService {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100113
114 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
Saurav Dasb4e3e102018-10-02 15:31:17 -0700115 private static final String HOST_LOC_PROVIDER =
116 "org.onosproject.provider.host.impl.HostLocationProvider";
Amit Ghosh47243cb2017-07-26 05:08:53 +0100117 private final Logger log = LoggerFactory.getLogger(getClass());
118 private final InternalConfigListener cfgListener =
119 new InternalConfigListener();
120
121 private final Set<ConfigFactory> factories = ImmutableSet.of(
122 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
123 DhcpL2RelayConfig.class,
124 "dhcpl2relay") {
125 @Override
126 public DhcpL2RelayConfig createConfig() {
127 return new DhcpL2RelayConfig();
128 }
129 }
130 );
131
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100133 protected NetworkConfigRegistry cfgService;
134
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100136 protected CoreService coreService;
137
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100139 protected PacketService packetService;
140
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100142 protected HostService hostService;
143
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100145 protected ComponentConfigService componentConfigService;
146
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Gamze Abakac806c6c2018-12-03 12:49:46 +0000148 protected SadisService sadisService;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100149
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100151 protected DeviceService deviceService;
152
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh8951f042017-08-10 13:48:10 +0100154 protected MastershipService mastershipService;
155
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Saurav Dasb4e3e102018-10-02 15:31:17 -0700157 protected FlowObjectiveService flowObjectiveService;
158
Carmelo Cascone4330cf12019-11-15 21:34:02 -0800159 /** Add option 82 to relayed packets. */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700160 protected boolean option82 = OPTION_82_DEFAULT;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100161
Carmelo Cascone4330cf12019-11-15 21:34:02 -0800162 /** Ask the DHCP Server to send back replies as L2 broadcast. */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700163 protected boolean enableDhcpBroadcastReplies = ENABLE_DHCP_BROADCAST_REPLIES_DEFAULT;
Amit Ghosha17354e2017-08-23 12:56:04 +0100164
Amit Ghosh47243cb2017-07-26 05:08:53 +0100165 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
166 new DhcpRelayPacketProcessor();
167
Amit Ghosh8951f042017-08-10 13:48:10 +0100168 private InnerMastershipListener changeListener = new InnerMastershipListener();
169 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100170
Amit Ghosh8951f042017-08-10 13:48:10 +0100171 // connect points to the DHCP server
172 Set<ConnectPoint> dhcpConnectPoints;
173 private AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100174 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
175 private ApplicationId appId;
176
Amit Ghosha17354e2017-08-23 12:56:04 +0100177 static Map<String, DhcpAllocationInfo> allocationMap = Maps.newConcurrentMap();
Amit Ghosh83c8c892017-11-09 11:08:27 +0000178 private boolean modifyClientPktsSrcDstMac = false;
179 //Whether to use the uplink port of the OLTs to send/receive messages to the DHCP server
180 private boolean useOltUplink = false;
Amit Ghosha17354e2017-08-23 12:56:04 +0100181
Gamze Abakac806c6c2018-12-03 12:49:46 +0000182 private BaseInformationService<SubscriberAndDeviceInformation> subsService;
183
Amit Ghosh47243cb2017-07-26 05:08:53 +0100184 @Activate
185 protected void activate(ComponentContext context) {
186 //start the dhcp relay agent
187 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700188 // ensure that host-learning via dhcp includes IP addresses
189 componentConfigService.preSetProperty(HOST_LOC_PROVIDER,
190 "useDhcp", Boolean.TRUE.toString());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100191 componentConfigService.registerProperties(getClass());
Jonathan Hartc36c9552018-07-31 15:07:53 -0400192 eventDispatcher.addSink(DhcpL2RelayEvent.class, listenerRegistry);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100193
194 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100195 mastershipService.addListener(changeListener);
196 deviceService.addListener(deviceListener);
197
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700198 subsService = sadisService.getSubscriberInfoService();
199
Amit Ghosh47243cb2017-07-26 05:08:53 +0100200 factories.forEach(cfgService::registerConfigFactory);
201 //update the dhcp server configuration.
202 updateConfig();
203 //add the packet services.
204 packetService.addProcessor(dhcpRelayPacketProcessor,
205 PacketProcessor.director(0));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000206 if (context != null) {
207 modified(context);
208 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100209
Gamze Abakac806c6c2018-12-03 12:49:46 +0000210
Amit Ghosh47243cb2017-07-26 05:08:53 +0100211 log.info("DHCP-L2-RELAY Started");
212 }
213
214 @Deactivate
215 protected void deactivate() {
216 cfgService.removeListener(cfgListener);
217 factories.forEach(cfgService::unregisterConfigFactory);
218 packetService.removeProcessor(dhcpRelayPacketProcessor);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700219 cancelDhcpPktsFromServer();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100220
221 componentConfigService.unregisterProperties(getClass(), false);
Deepa Vaddireddy77a6ac72017-09-20 20:36:52 +0530222 deviceService.removeListener(deviceListener);
223 mastershipService.removeListener(changeListener);
Jonathan Hartc36c9552018-07-31 15:07:53 -0400224 eventDispatcher.removeSink(DhcpL2RelayEvent.class);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100225 log.info("DHCP-L2-RELAY Stopped");
226 }
227
228 @Modified
229 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000230
Amit Ghosh47243cb2017-07-26 05:08:53 +0100231 Dictionary<?, ?> properties = context.getProperties();
232
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700233 Boolean o = Tools.isPropertyEnabled(properties, OPTION_82);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100234 if (o != null) {
235 option82 = o;
236 }
Amit Ghosh2095dc62017-09-25 20:56:55 +0100237
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700238 o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_BROADCAST_REPLIES);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100239 if (o != null) {
240 enableDhcpBroadcastReplies = o;
241 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100242 }
243
244 /**
245 * Checks if this app has been configured.
246 *
247 * @return true if all information we need have been initialized
248 */
249 private boolean configured() {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000250 if (!useOltUplink) {
251 return dhcpServerConnectPoint.get() != null;
252 }
253 return true;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100254 }
255
Amit Ghosh8951f042017-08-10 13:48:10 +0100256 /**
257 * Selects a connect point through an available device for which it is the master.
258 */
259 private void selectServerConnectPoint() {
260 synchronized (this) {
261 dhcpServerConnectPoint.set(null);
262 if (dhcpConnectPoints != null) {
263 // find a connect point through a device for which we are master
264 for (ConnectPoint cp: dhcpConnectPoints) {
265 if (mastershipService.isLocalMaster(cp.deviceId())) {
266 if (deviceService.isAvailable(cp.deviceId())) {
267 dhcpServerConnectPoint.set(cp);
268 }
269 log.info("DHCP connectPoint selected is {}", cp);
270 break;
271 }
272 }
273 }
274
275 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
276
277 if (dhcpServerConnectPoint.get() == null) {
278 log.error("Master of none, can't relay DHCP Message to server");
279 }
280 }
281 }
282
283 /**
284 * Updates the network configuration.
285 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100286 private void updateConfig() {
287 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
288 if (cfg == null) {
289 log.warn("Dhcp Server info not available");
290 return;
291 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100292
293 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
Amit Ghosh83c8c892017-11-09 11:08:27 +0000294 modifyClientPktsSrcDstMac = cfg.getModifySrcDstMacAddresses();
Saurav Dasb4e3e102018-10-02 15:31:17 -0700295 boolean prevUseOltUplink = useOltUplink;
Amit Ghosh83c8c892017-11-09 11:08:27 +0000296 useOltUplink = cfg.getUseOltUplinkForServerPktInOut();
Amit Ghosh8951f042017-08-10 13:48:10 +0100297
Saurav Dasb4e3e102018-10-02 15:31:17 -0700298 if (useOltUplink) {
299 for (ConnectPoint cp : getUplinkPortsOfOlts()) {
300 log.debug("requestDhcpPackets: ConnectPoint: {}", cp);
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700301 requestDhcpPacketsFromConnectPoint(cp, Optional.ofNullable(null));
Saurav Dasb4e3e102018-10-02 15:31:17 -0700302 }
303 // check if previous config was different and so trap flows may
Saurav Dasb14f08a2019-02-22 16:34:15 -0800304 // need to be removed from other places like AGG switches
Saurav Dasb4e3e102018-10-02 15:31:17 -0700305 if (!prevUseOltUplink) {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800306 addOrRemoveDhcpTrapFromServer(false);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700307 }
Saurav Dasb4e3e102018-10-02 15:31:17 -0700308 } else {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800309 // uplink on AGG switch
310 addOrRemoveDhcpTrapFromServer(true);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700311 }
312 }
313
314 private void cancelDhcpPktsFromServer() {
315 if (useOltUplink) {
316 for (ConnectPoint cp : getUplinkPortsOfOlts()) {
317 log.debug("cancelDhcpPackets: ConnectPoint: {}", cp);
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700318 cancelDhcpPacketsFromConnectPoint(cp, Optional.ofNullable(null));
Saurav Dasb4e3e102018-10-02 15:31:17 -0700319 }
320 } else {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800321 // uplink on AGG switch
322 addOrRemoveDhcpTrapFromServer(false);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000323 }
Saurav Dasb4e3e102018-10-02 15:31:17 -0700324 }
325
Saurav Dasb14f08a2019-02-22 16:34:15 -0800326 /**
327 * Used to add or remove DHCP trap flow for packets received from DHCP server.
328 * Typically used on a non OLT device, like an AGG switch. When adding, a
329 * new dhcp server connect point is selected from the configured options.
330 *
331 * @param add true if dhcp trap flow is to be added, false to remove the
332 * trap flow
333 */
334 private void addOrRemoveDhcpTrapFromServer(boolean add) {
335 if (add) {
336 selectServerConnectPoint();
337 log.debug("dhcp server connect point: " + dhcpServerConnectPoint);
338 }
339 if (dhcpServerConnectPoint.get() == null) {
340 log.warn("No dhcpServer connectPoint found, cannot {} dhcp trap flows",
341 (add) ? "install" : "remove");
342 return;
343 }
344 if (add) {
345 log.info("Adding trap to dhcp server connect point: "
346 + dhcpServerConnectPoint);
347 requestDhcpPacketsFromConnectPoint(dhcpServerConnectPoint.get(),
348 Optional.of(PacketPriority.HIGH1));
349 } else {
350 log.info("Removing trap from dhcp server connect point: "
351 + dhcpServerConnectPoint);
352 cancelDhcpPacketsFromConnectPoint(dhcpServerConnectPoint.get(),
353 Optional.of(PacketPriority.HIGH1));
354 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100355 }
356
357 /**
Amit Ghosh83c8c892017-11-09 11:08:27 +0000358 * Returns all the uplink ports of OLTs configured in SADIS.
359 * Only ports visible in ONOS and for which this instance is master
360 * are returned
361 */
362 private List<ConnectPoint> getUplinkPortsOfOlts() {
363 List<ConnectPoint> cps = new ArrayList<>();
364
365 // find all the olt devices and if their uplink ports are visible
366 Iterable<Device> devices = deviceService.getDevices();
367 for (Device d : devices) {
368 // check if this device is provisioned in Sadis
369
370 log.debug("getUplinkPortsOfOlts: Checking mastership of {}", d);
371 // do only for devices for which we are the master
372 if (!mastershipService.isLocalMaster(d.id())) {
373 continue;
374 }
375
376 String devSerialNo = d.serialNumber();
377 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
378 log.debug("getUplinkPortsOfOlts: Found device: {}", deviceInfo);
379 if (deviceInfo != null) {
380 // check if the uplink port with that number is available on the device
381 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
382 Port port = deviceService.getPort(d.id(), pNum);
383 log.debug("getUplinkPortsOfOlts: Found port: {}", port);
384 if (port != null) {
385 cps.add(new ConnectPoint(d.id(), pNum));
386 }
387 }
388 }
389 return cps;
390 }
391
392 /**
393 * Returns whether the passed port is the uplink port of the olt device.
394 */
395 private boolean isUplinkPortOfOlt(DeviceId dId, Port p) {
396 log.debug("isUplinkPortOfOlt: DeviceId: {} Port: {}", dId, p);
397 // do only for devices for which we are the master
398 if (!mastershipService.isLocalMaster(dId)) {
399 return false;
400 }
401
402 Device d = deviceService.getDevice(dId);
403 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
404
405 if (deviceInfo != null) {
406 return (deviceInfo.uplinkPort() == p.number().toLong());
407 }
408
409 return false;
410 }
411
412 /**
413 * Returns the connectPoint which is the uplink port of the OLT.
414 */
415 private ConnectPoint getUplinkConnectPointOfOlt(DeviceId dId) {
416
417 Device d = deviceService.getDevice(dId);
418 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
419 log.debug("getUplinkConnectPointOfOlt DeviceId: {} devInfo: {}", dId, deviceInfo);
420 if (deviceInfo != null) {
421 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
422 Port port = deviceService.getPort(d.id(), pNum);
423 if (port != null) {
424 return new ConnectPoint(d.id(), pNum);
425 }
426 }
427
428 return null;
429 }
430
431 /**
432 * Request DHCP packet from particular connect point via PacketService.
Saurav Dasb14f08a2019-02-22 16:34:15 -0800433 * Optionally provide a priority for the trap flow. If no such priority is
434 * provided, the default priority will be used.
435 *
436 * @param cp the connect point to trap dhcp packets from
437 * @param priority of the trap flow, null to use default priority
Amit Ghosh83c8c892017-11-09 11:08:27 +0000438 */
Saurav Dasb14f08a2019-02-22 16:34:15 -0800439 private void requestDhcpPacketsFromConnectPoint(ConnectPoint cp,
440 Optional<PacketPriority> priority) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000441 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
442 .matchEthType(Ethernet.TYPE_IPV4)
443 .matchInPort(cp.port())
444 .matchIPProtocol(IPv4.PROTOCOL_UDP)
445 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
446 packetService.requestPackets(selectorServer.build(),
Saurav Dasb14f08a2019-02-22 16:34:15 -0800447 priority.isPresent() ? priority.get() : PacketPriority.CONTROL,
448 appId, Optional.of(cp.deviceId()));
Amit Ghosh83c8c892017-11-09 11:08:27 +0000449 }
450
451 /**
Saurav Dasb14f08a2019-02-22 16:34:15 -0800452 * Cancel DHCP packet from particular connect point via PacketService. If
453 * the request was made with a specific packet priority, then the same
454 * priority should be used in this call.
455 *
456 * @param cp the connect point for the trap flow
457 * @param priority with which the trap flow was requested; if request
458 * priority was not specified, this param should also be null
Amit Ghosh83c8c892017-11-09 11:08:27 +0000459 */
Saurav Dasb14f08a2019-02-22 16:34:15 -0800460 private void cancelDhcpPacketsFromConnectPoint(ConnectPoint cp,
461 Optional<PacketPriority> priority) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000462 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
463 .matchEthType(Ethernet.TYPE_IPV4)
464 .matchInPort(cp.port())
465 .matchIPProtocol(IPv4.PROTOCOL_UDP)
466 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
467 packetService.cancelPackets(selectorServer.build(),
Saurav Dasb14f08a2019-02-22 16:34:15 -0800468 priority.isPresent() ? priority.get() : PacketPriority.CONTROL,
469 appId, Optional.of(cp.deviceId()));
Amit Ghosh83c8c892017-11-09 11:08:27 +0000470 }
471
Amit Ghosha17354e2017-08-23 12:56:04 +0100472 public static Map<String, DhcpAllocationInfo> allocationMap() {
473 return allocationMap;
474 }
475
Amit Ghosh47243cb2017-07-26 05:08:53 +0100476 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
477 String serialNo = deviceService.getDevice(context.inPacket().
478 receivedFrom().deviceId()).serialNumber();
479
480 return subsService.get(serialNo);
481 }
482
483 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
484 String serialNo = deviceService.getDevice(cp.deviceId()).
485 serialNumber();
486
487 return subsService.get(serialNo);
488 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100489
490 private MacAddress relayAgentMacAddress(PacketContext context) {
491
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000492 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100493 if (device == null) {
494 log.warn("Device not found for {}", context.inPacket().
495 receivedFrom());
496 return null;
497 }
498
499 return device.hardwareIdentifier();
500 }
501
502 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100503 return nasPortId(context.inPacket().receivedFrom());
504 }
505
506 private String nasPortId(ConnectPoint cp) {
507 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100508 return p.annotations().value(AnnotationKeys.PORT_NAME);
509 }
510
511 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100512 return subsService.get(nasPortId(context));
513 }
514
515 private VlanId cTag(PacketContext context) {
516 SubscriberAndDeviceInformation sub = getSubscriber(context);
517 if (sub == null) {
518 log.warn("Subscriber info not found for {}", context.inPacket().
519 receivedFrom());
520 return VlanId.NONE;
521 }
522 return sub.cTag();
523 }
524
Amit Ghosh8951f042017-08-10 13:48:10 +0100525 private VlanId cTag(ConnectPoint cp) {
526 String portId = nasPortId(cp);
527 SubscriberAndDeviceInformation sub = subsService.get(portId);
528 if (sub == null) {
529 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
530 return VlanId.NONE;
531 }
532 return sub.cTag();
533 }
534
535 private VlanId sTag(ConnectPoint cp) {
536 String portId = nasPortId(cp);
537 SubscriberAndDeviceInformation sub = subsService.get(portId);
538 if (sub == null) {
539 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
540 return VlanId.NONE;
541 }
542 return sub.sTag();
543 }
544
Amit Ghosh47243cb2017-07-26 05:08:53 +0100545 private VlanId sTag(PacketContext context) {
546 SubscriberAndDeviceInformation sub = getSubscriber(context);
547 if (sub == null) {
548 log.warn("Subscriber info not found for {}", context.inPacket().
549 receivedFrom());
550 return VlanId.NONE;
551 }
552 return sub.sTag();
553 }
554
555 private class DhcpRelayPacketProcessor implements PacketProcessor {
556
557 @Override
558 public void process(PacketContext context) {
559 if (!configured()) {
560 log.warn("Missing DHCP relay config. Abort packet processing");
561 return;
562 }
563
564 // process the packet and get the payload
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530565 Ethernet packet = context.inPacket().parsed();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000566
Amit Ghosh47243cb2017-07-26 05:08:53 +0100567 if (packet == null) {
568 log.warn("Packet is null");
569 return;
570 }
571
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530572 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100573 IPv4 ipv4Packet = (IPv4) packet.getPayload();
574
575 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
576 UDP udpPacket = (UDP) ipv4Packet.getPayload();
577 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000578 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100579 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
580 //This packet is dhcp.
581 processDhcpPacket(context, packet, dhcpPayload);
582 }
583 }
584 }
585 }
586
587 //forward the packet to ConnectPoint where the DHCP server is attached.
Amit Ghosh83c8c892017-11-09 11:08:27 +0000588 private void forwardPacket(Ethernet packet, PacketContext context) {
589 ConnectPoint toSendTo = null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100590
Amit Ghosh83c8c892017-11-09 11:08:27 +0000591 if (!useOltUplink) {
592 toSendTo = dhcpServerConnectPoint.get();
593 } else {
594 toSendTo = getUplinkConnectPointOfOlt(context.inPacket().
595 receivedFrom().deviceId());
596 }
597
598 if (toSendTo != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100599 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh83c8c892017-11-09 11:08:27 +0000600 .setOutput(toSendTo.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100601 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh83c8c892017-11-09 11:08:27 +0000602 toSendTo.deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100603 ByteBuffer.wrap(packet.serialize()));
604 if (log.isTraceEnabled()) {
Saurav Das15626a02018-09-27 18:36:45 -0700605 log.trace("Relaying packet to dhcp server at {} {}",
606 toSendTo, packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100607 }
608 packetService.emit(o);
609 } else {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000610 log.error("No connect point to send msg to DHCP Server");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100611 }
612 }
613
Amit Ghosha17354e2017-08-23 12:56:04 +0100614 // get the type of the DHCP packet
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700615 private DHCP.MsgType getDhcpPacketType(DHCP dhcpPayload) {
Amit Ghosha17354e2017-08-23 12:56:04 +0100616
Jonathan Hartedbf6422018-05-02 17:30:05 -0700617 for (DhcpOption option : dhcpPayload.getOptions()) {
Amit Ghosha17354e2017-08-23 12:56:04 +0100618 if (option.getCode() == OptionCode_MessageType.getValue()) {
619 byte[] data = option.getData();
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700620 return DHCP.MsgType.getType(data[0]);
Amit Ghosha17354e2017-08-23 12:56:04 +0100621 }
622 }
623 return null;
624 }
625
Amit Ghosh47243cb2017-07-26 05:08:53 +0100626 //process the dhcp packet before sending to server
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530627 private void processDhcpPacket(PacketContext context, Ethernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100628 DHCP dhcpPayload) {
629 if (dhcpPayload == null) {
630 log.warn("DHCP payload is null");
631 return;
632 }
633
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700634 DHCP.MsgType incomingPacketType = getDhcpPacketType(dhcpPayload);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000635
Saurav Das15626a02018-09-27 18:36:45 -0700636 log.info("Received DHCP Packet of type {} from {}",
637 incomingPacketType, context.inPacket().receivedFrom());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100638
639 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000640 case DHCPDISCOVER:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530641 Ethernet ethernetPacketDiscover =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000642 processDhcpPacketFromClient(context, packet);
643 if (ethernetPacketDiscover != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000644 forwardPacket(ethernetPacketDiscover, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000645 }
646 break;
647 case DHCPOFFER:
648 //reply to dhcp client.
Saurav Das15626a02018-09-27 18:36:45 -0700649 Ethernet ethernetPacketOffer =
650 processDhcpPacketFromServer(context, packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000651 if (ethernetPacketOffer != null) {
652 sendReply(ethernetPacketOffer, dhcpPayload);
653 }
654 break;
655 case DHCPREQUEST:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530656 Ethernet ethernetPacketRequest =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000657 processDhcpPacketFromClient(context, packet);
658 if (ethernetPacketRequest != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000659 forwardPacket(ethernetPacketRequest, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000660 }
661 break;
662 case DHCPACK:
663 //reply to dhcp client.
Saurav Das15626a02018-09-27 18:36:45 -0700664 Ethernet ethernetPacketAck =
665 processDhcpPacketFromServer(context, packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000666 if (ethernetPacketAck != null) {
667 sendReply(ethernetPacketAck, dhcpPayload);
668 }
669 break;
670 default:
671 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100672 }
673 }
674
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530675 private Ethernet processDhcpPacketFromClient(PacketContext context,
676 Ethernet ethernetPacket) {
Saurav Das15626a02018-09-27 18:36:45 -0700677 if (log.isTraceEnabled()) {
678 log.trace("DHCP packet received from client at {} {}",
679 context.inPacket().receivedFrom(), ethernetPacket);
680 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100681
682 MacAddress relayAgentMac = relayAgentMacAddress(context);
683 if (relayAgentMac == null) {
684 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100685 return null;
686 }
687
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530688 Ethernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100689
690 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
691 UDP udpPacket = (UDP) ipv4Packet.getPayload();
692 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
693
Amit Ghosha17354e2017-08-23 12:56:04 +0100694 if (enableDhcpBroadcastReplies) {
695 // We want the reply to come back as a L2 broadcast
696 dhcpPacket.setFlags((short) 0x8000);
697 }
698
Jonathan Hartc36c9552018-07-31 15:07:53 -0400699 MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
700 IpAddress clientIp = IpAddress.valueOf(dhcpPacket.getClientIPAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100701
Jonathan Hartc36c9552018-07-31 15:07:53 -0400702 SubscriberAndDeviceInformation entry = getSubscriber(context);
703 if (entry == null) {
Saurav Das15626a02018-09-27 18:36:45 -0700704 log.warn("Dropping packet as subscriber entry is not available");
Jonathan Hartc36c9552018-07-31 15:07:53 -0400705 return null;
706 }
707
708 DhcpAllocationInfo info = new DhcpAllocationInfo(
709 context.inPacket().receivedFrom(), dhcpPacket.getPacketType(),
710 entry.nasPortId(), clientMac, clientIp);
711
712 allocationMap.put(entry.nasPortId(), info);
713
Saurav Das15626a02018-09-27 18:36:45 -0700714 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.UPDATED, info,
715 context.inPacket().receivedFrom()));
Amit Ghosha17354e2017-08-23 12:56:04 +0100716
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000717 if (option82) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000718 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
719 udpPacket.setPayload(dhcpPacketWithOption82);
720 }
721
722 ipv4Packet.setPayload(udpPacket);
723 etherReply.setPayload(ipv4Packet);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000724 if (modifyClientPktsSrcDstMac) {
725 etherReply.setSourceMACAddress(relayAgentMac);
726 etherReply.setDestinationMACAddress(dhcpConnectMac);
727 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100728
Amit Ghosh8951f042017-08-10 13:48:10 +0100729 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100730 etherReply.setVlanID(cTag(context).toShort());
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530731 etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
732 etherReply.setQinQVID(sTag(context).toShort());
Saurav Das15626a02018-09-27 18:36:45 -0700733 log.info("Finished processing packet.. relaying to dhcpServer");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100734 return etherReply;
735 }
736
737 //build the DHCP offer/ack with proper client port.
Saurav Das15626a02018-09-27 18:36:45 -0700738 private Ethernet processDhcpPacketFromServer(PacketContext context,
739 Ethernet ethernetPacket) {
740 if (log.isTraceEnabled()) {
741 log.trace("DHCP packet received from server at {} {}",
742 context.inPacket().receivedFrom(), ethernetPacket);
743 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100744 // get dhcp header.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530745 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100746 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
747 UDP udpPacket = (UDP) ipv4Packet.getPayload();
748 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
749
Amit Ghosh47243cb2017-07-26 05:08:53 +0100750 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100751 ConnectPoint subsCp = getConnectPointOfClient(dstMac);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100752 // If we can't find the subscriber, can't process further
753 if (subsCp == null) {
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700754 log.warn("Couldn't find connection point for mac address {} DHCPOFFERs won't be delivered", dstMac);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100755 return null;
756 }
Amit Ghosha17354e2017-08-23 12:56:04 +0100757 // if it's an ACK packet store the information for display purpose
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700758 if (getDhcpPacketType(dhcpPayload) == DHCP.MsgType.DHCPACK) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100759
Amit Ghosha17354e2017-08-23 12:56:04 +0100760 String portId = nasPortId(subsCp);
761 SubscriberAndDeviceInformation sub = subsService.get(portId);
762 if (sub != null) {
Jonathan Hartedbf6422018-05-02 17:30:05 -0700763 List<DhcpOption> options = dhcpPayload.getOptions();
764 List<DhcpOption> circuitIds = options.stream()
Amit Ghosha17354e2017-08-23 12:56:04 +0100765 .filter(option -> option.getCode() == DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
766 .collect(Collectors.toList());
767
768 String circuitId = "None";
769 if (circuitIds.size() == 1) {
Amit Ghosh2095dc62017-09-25 20:56:55 +0100770 byte[] array = circuitIds.get(0).getData();
771
772 try {
773 // we leave the first two bytes as they are the id and length
774 circuitId = new String(Arrays.copyOfRange(array, 2, array.length), "UTF-8");
775 } catch (Exception e) { }
Amit Ghosha17354e2017-08-23 12:56:04 +0100776 }
777
778 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
779
780 //storeDHCPAllocationInfo
Jonathan Hartc36c9552018-07-31 15:07:53 -0400781 DhcpAllocationInfo info = new DhcpAllocationInfo(subsCp,
782 dhcpPayload.getPacketType(), circuitId, dstMac, ip);
Amit Ghosha17354e2017-08-23 12:56:04 +0100783
784 allocationMap.put(sub.nasPortId(), info);
Jonathan Hartc36c9552018-07-31 15:07:53 -0400785
786 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.UPDATED, info, subsCp));
Amit Ghosha17354e2017-08-23 12:56:04 +0100787 }
788 } // end storing of info
Amit Ghosh47243cb2017-07-26 05:08:53 +0100789
790 // we leave the srcMac from the original packet
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530791 etherReply.setDestinationMACAddress(dstMac);
792 etherReply.setQinQVID(sTag(subsCp).toShort());
Amit Ghosh8951f042017-08-10 13:48:10 +0100793 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
794 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100795
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000796 if (option82) {
797 udpPacket.setPayload(removeOption82(dhcpPayload));
798 } else {
799 udpPacket.setPayload(dhcpPayload);
800 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100801 ipv4Packet.setPayload(udpPacket);
802 etherReply.setPayload(ipv4Packet);
803
Saurav Das15626a02018-09-27 18:36:45 -0700804 log.info("Finished processing packet.. relaying to client");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100805 return etherReply;
806 }
807
Amit Ghosha17354e2017-08-23 12:56:04 +0100808 /*
809 * Get ConnectPoint of the Client based on it's MAC address
810 */
811 private ConnectPoint getConnectPointOfClient(MacAddress dstMac) {
812 Set<Host> hosts = hostService.getHostsByMac(dstMac);
813 if (hosts == null || hosts.isEmpty()) {
814 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530815 + "relay for dhcp packet from server",
Amit Ghosha17354e2017-08-23 12:56:04 +0100816 dstMac);
817 return null;
818 }
819 for (Host h : hosts) {
820 // if more than one,
821 // find the connect point which has an valid entry in SADIS
822 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
823 h.location().port());
824
825 if (sTag(cp) != VlanId.NONE) {
826 return cp;
827 }
828 }
829
830 return null;
831 }
832
Amit Ghosh47243cb2017-07-26 05:08:53 +0100833 //send the response to the requester host.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530834 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100835 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100836 ConnectPoint subCp = getConnectPointOfClient(descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100837
838 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +0100839 if (subCp != null) {
Saurav Das15626a02018-09-27 18:36:45 -0700840 log.info("Sending DHCP packet to client at {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100841 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +0100842 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100843 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +0100844 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100845 if (log.isTraceEnabled()) {
Saurav Das15626a02018-09-27 18:36:45 -0700846 log.trace("Relaying packet to dhcp client at {} {}", subCp,
847 ethPacket);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100848 }
849 packetService.emit(o);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100850 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000851 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100852 }
853 }
854 }
855
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000856 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
857 log.debug("option82data {} ", entry);
858
Jonathan Hartedbf6422018-05-02 17:30:05 -0700859 List<DhcpOption> options = Lists.newArrayList(dhcpPacket.getOptions());
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000860 DhcpOption82 option82 = new DhcpOption82();
861 option82.setAgentCircuitId(entry.circuitId());
862 option82.setAgentRemoteId(entry.remoteId());
Jonathan Hartedbf6422018-05-02 17:30:05 -0700863 DhcpOption option = new DhcpOption()
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530864 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
865 .setData(option82.toByteArray())
866 .setLength(option82.length());
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000867
868 options.add(options.size() - 1, option);
869 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +0100870
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000871 return dhcpPacket;
872
873 }
874
875 private DHCP removeOption82(DHCP dhcpPacket) {
Jonathan Hartedbf6422018-05-02 17:30:05 -0700876 List<DhcpOption> options = dhcpPacket.getOptions();
877 List<DhcpOption> newoptions = options.stream()
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000878 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
879 .collect(Collectors.toList());
880
881 return dhcpPacket.setOptions(newoptions);
882 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100883 /**
884 * Listener for network config events.
885 */
886 private class InternalConfigListener implements NetworkConfigListener {
887
888 @Override
889 public void event(NetworkConfigEvent event) {
890
891 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
892 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
893 event.configClass().equals(DhcpL2RelayConfig.class)) {
894 updateConfig();
895 log.info("Reconfigured");
896 }
897 }
898 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000899
Amit Ghosh8951f042017-08-10 13:48:10 +0100900 /**
901 * Handles Mastership changes for the devices which connect
902 * to the DHCP server.
903 */
904 private class InnerMastershipListener implements MastershipListener {
905 @Override
906 public void event(MastershipEvent event) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000907 if (!useOltUplink) {
908 if (dhcpServerConnectPoint.get() != null &&
909 dhcpServerConnectPoint.get().deviceId().
910 equals(event.subject())) {
911 log.trace("Mastership Event recevived for {}", event.subject());
912 // mastership of the device for our connect point has changed
913 // reselect
914 selectServerConnectPoint();
915 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100916 }
917 }
918 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000919
Amit Ghosh8951f042017-08-10 13:48:10 +0100920 /**
921 * Handles Device status change for the devices which connect
922 * to the DHCP server.
923 */
924 private class InnerDeviceListener implements DeviceListener {
925 @Override
926 public void event(DeviceEvent event) {
Saurav Das15626a02018-09-27 18:36:45 -0700927 if (log.isTraceEnabled() &&
928 !event.type().equals(DeviceEvent.Type.PORT_STATS_UPDATED)) {
929 log.trace("Device Event received for {} event {}",
930 event.subject(), event.type());
931 }
Amit Ghosh83c8c892017-11-09 11:08:27 +0000932 if (!useOltUplink) {
933 if (dhcpServerConnectPoint.get() == null) {
934 switch (event.type()) {
935 case DEVICE_ADDED:
936 case DEVICE_AVAILABILITY_CHANGED:
Saurav Dasb14f08a2019-02-22 16:34:15 -0800937 // some device is available check if we can get a
938 // connect point we can use
939 addOrRemoveDhcpTrapFromServer(true);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000940 break;
941 default:
942 break;
943 }
944 return;
Amit Ghosh8951f042017-08-10 13:48:10 +0100945 }
Amit Ghosh83c8c892017-11-09 11:08:27 +0000946 if (dhcpServerConnectPoint.get().deviceId().
947 equals(event.subject().id())) {
948 switch (event.type()) {
949 case DEVICE_AVAILABILITY_CHANGED:
950 case DEVICE_REMOVED:
951 case DEVICE_SUSPENDED:
952 // state of our device has changed, check if we need
Saurav Dasb14f08a2019-02-22 16:34:15 -0800953 // to re-select a connectpoint
954 addOrRemoveDhcpTrapFromServer(true);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000955 break;
956 default:
957 break;
958 }
959 }
960 } else {
Amit Ghosh8951f042017-08-10 13:48:10 +0100961 switch (event.type()) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000962 case PORT_ADDED:
Saurav Dasb4e3e102018-10-02 15:31:17 -0700963 if (useOltUplink && isUplinkPortOfOlt(event.subject().id(), event.port())) {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800964 requestDhcpPacketsFromConnectPoint(
965 new ConnectPoint(event.subject().id(), event.port().number()),
966 null);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000967 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100968 break;
969 default:
970 break;
971 }
972 }
973 }
974 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100975}