blob: 8e11002098f4ff497dc72568b367039f1884c36b [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
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -080018import com.google.common.base.Strings;
19import com.google.common.collect.ImmutableMap;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Lists;
22import com.google.common.collect.Sets;
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -030023import org.apache.commons.io.HexDump;
Amit Ghosh47243cb2017-07-26 05:08:53 +010024import org.onlab.packet.DHCP;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000025import org.onlab.packet.Ethernet;
Amit Ghosh47243cb2017-07-26 05:08:53 +010026import org.onlab.packet.IPv4;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053027import org.onlab.packet.IpAddress;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000028import org.onlab.packet.MacAddress;
Amit Ghosh47243cb2017-07-26 05:08:53 +010029import org.onlab.packet.TpPort;
30import org.onlab.packet.UDP;
31import org.onlab.packet.VlanId;
Jonathan Hartedbf6422018-05-02 17:30:05 -070032import org.onlab.packet.dhcp.DhcpOption;
Jonathan Hart617bc3e2020-02-14 10:42:23 -080033import org.onlab.util.KryoNamespace;
Amit Ghosh47243cb2017-07-26 05:08:53 +010034import org.onlab.util.Tools;
35import org.onosproject.cfg.ComponentConfigService;
Jonathan Hart617bc3e2020-02-14 10:42:23 -080036import org.onosproject.cluster.ClusterService;
37import org.onosproject.cluster.LeadershipService;
Andrea Campanella6f45a1b2020-05-08 17:50:12 +020038import org.onosproject.cluster.NodeId;
Amit Ghosh47243cb2017-07-26 05:08:53 +010039import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
Jonathan Hartc36c9552018-07-31 15:07:53 -040041import org.onosproject.event.AbstractListenerManager;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053042import org.onosproject.mastership.MastershipEvent;
43import org.onosproject.mastership.MastershipListener;
44import org.onosproject.mastership.MastershipService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010045import org.onosproject.net.AnnotationKeys;
46import org.onosproject.net.ConnectPoint;
Amit Ghosh83c8c892017-11-09 11:08:27 +000047import org.onosproject.net.Device;
48import org.onosproject.net.DeviceId;
Amit Ghosh47243cb2017-07-26 05:08:53 +010049import org.onosproject.net.Host;
50import org.onosproject.net.Port;
Amit Ghosh83c8c892017-11-09 11:08:27 +000051import org.onosproject.net.PortNumber;
Amit Ghosh47243cb2017-07-26 05:08:53 +010052import org.onosproject.net.config.ConfigFactory;
53import org.onosproject.net.config.NetworkConfigEvent;
54import org.onosproject.net.config.NetworkConfigListener;
55import org.onosproject.net.config.NetworkConfigRegistry;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053056import org.onosproject.net.device.DeviceEvent;
57import org.onosproject.net.device.DeviceListener;
Amit Ghosh47243cb2017-07-26 05:08:53 +010058import org.onosproject.net.device.DeviceService;
59import org.onosproject.net.flow.DefaultTrafficSelector;
60import org.onosproject.net.flow.DefaultTrafficTreatment;
61import org.onosproject.net.flow.TrafficSelector;
62import org.onosproject.net.flow.TrafficTreatment;
Saurav Dasb4e3e102018-10-02 15:31:17 -070063import org.onosproject.net.flowobjective.FlowObjectiveService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010064import org.onosproject.net.host.HostService;
65import org.onosproject.net.packet.DefaultOutboundPacket;
66import org.onosproject.net.packet.OutboundPacket;
67import org.onosproject.net.packet.PacketContext;
68import org.onosproject.net.packet.PacketPriority;
69import org.onosproject.net.packet.PacketProcessor;
70import org.onosproject.net.packet.PacketService;
Jonathan Hart617bc3e2020-02-14 10:42:23 -080071import org.onosproject.store.serializers.KryoNamespaces;
72import org.onosproject.store.service.ConsistentMap;
73import org.onosproject.store.service.Serializer;
74import org.onosproject.store.service.StorageService;
75import org.onosproject.store.service.Versioned;
Matteo Scandolo57af5d12019-04-29 17:11:41 -070076import org.opencord.dhcpl2relay.DhcpAllocationInfo;
77import org.opencord.dhcpl2relay.DhcpL2RelayEvent;
78import org.opencord.dhcpl2relay.DhcpL2RelayListener;
79import org.opencord.dhcpl2relay.DhcpL2RelayService;
Jonathan Hart77ca3152020-02-21 14:31:21 -080080import org.opencord.dhcpl2relay.DhcpL2RelayStoreDelegate;
Matteo Scandolo57af5d12019-04-29 17:11:41 -070081import org.opencord.dhcpl2relay.impl.packet.DhcpOption82;
Gamze Abakac806c6c2018-12-03 12:49:46 +000082import org.opencord.sadis.BaseInformationService;
83import org.opencord.sadis.SadisService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010084import org.opencord.sadis.SubscriberAndDeviceInformation;
Gamze Abakaa64b3bc2020-01-31 06:51:43 +000085import org.opencord.sadis.UniTagInformation;
Amit Ghosh47243cb2017-07-26 05:08:53 +010086import org.osgi.service.component.ComponentContext;
Carmelo Casconede1e6e32019-07-15 19:39:08 -070087import org.osgi.service.component.annotations.Activate;
88import org.osgi.service.component.annotations.Component;
89import org.osgi.service.component.annotations.Deactivate;
90import org.osgi.service.component.annotations.Modified;
91import org.osgi.service.component.annotations.Reference;
92import org.osgi.service.component.annotations.ReferenceCardinality;
Amit Ghosh47243cb2017-07-26 05:08:53 +010093import org.slf4j.Logger;
94import org.slf4j.LoggerFactory;
95
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -080096import java.io.ByteArrayOutputStream;
97import java.nio.ByteBuffer;
98import java.time.Instant;
99import java.util.ArrayList;
100import java.util.Dictionary;
101import java.util.List;
102import java.util.Map;
103import java.util.Optional;
104import java.util.Set;
105import java.util.UUID;
106import java.util.concurrent.ExecutorService;
107import java.util.concurrent.Executors;
108import java.util.concurrent.ScheduledExecutorService;
109import java.util.concurrent.ScheduledFuture;
110import java.util.concurrent.atomic.AtomicReference;
111import java.util.function.Predicate;
112import java.util.stream.Collectors;
113
114import static java.util.concurrent.Executors.newFixedThreadPool;
115import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
116import static org.onlab.packet.MacAddress.valueOf;
117import static org.onlab.util.Tools.groupedThreads;
118import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
119import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.*;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100120
121/**
122 * DHCP Relay Agent Application Component.
123 */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700124@Component(immediate = true,
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700125 property = {
126 OPTION_82 + ":Boolean=" + OPTION_82_DEFAULT,
127 ENABLE_DHCP_BROADCAST_REPLIES + ":Boolean=" + ENABLE_DHCP_BROADCAST_REPLIES_DEFAULT,
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800128 PACKET_PROCESSOR_THREADS + ":Integer=" + PACKET_PROCESSOR_THREADS_DEFAULT,
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700129 })
Jonathan Hartc36c9552018-07-31 15:07:53 -0400130public class DhcpL2Relay
131 extends AbstractListenerManager<DhcpL2RelayEvent, DhcpL2RelayListener>
132 implements DhcpL2RelayService {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100133
134 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
Saurav Dasb4e3e102018-10-02 15:31:17 -0700135 private static final String HOST_LOC_PROVIDER =
136 "org.onosproject.provider.host.impl.HostLocationProvider";
Amit Ghosh47243cb2017-07-26 05:08:53 +0100137 private final Logger log = LoggerFactory.getLogger(getClass());
138 private final InternalConfigListener cfgListener =
139 new InternalConfigListener();
140
141 private final Set<ConfigFactory> factories = ImmutableSet.of(
142 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700143 DhcpL2RelayConfig.class,
144 "dhcpl2relay") {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100145 @Override
146 public DhcpL2RelayConfig createConfig() {
147 return new DhcpL2RelayConfig();
148 }
149 }
150 );
151
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100153 protected NetworkConfigRegistry cfgService;
154
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100156 protected CoreService coreService;
157
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100159 protected PacketService packetService;
160
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100162 protected HostService hostService;
163
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100165 protected ComponentConfigService componentConfigService;
166
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Gamze Abakac806c6c2018-12-03 12:49:46 +0000168 protected SadisService sadisService;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100169
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100171 protected DeviceService deviceService;
172
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh8951f042017-08-10 13:48:10 +0100174 protected MastershipService mastershipService;
175
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart617bc3e2020-02-14 10:42:23 -0800177 protected StorageService storageService;
178
179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Saurav Dasb4e3e102018-10-02 15:31:17 -0700180 protected FlowObjectiveService flowObjectiveService;
181
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
183 protected DhcpL2RelayCountersStore dhcpL2RelayCounters;
184
Jonathan Hart617bc3e2020-02-14 10:42:23 -0800185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
186 protected LeadershipService leadershipService;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
189 protected ClusterService clusterService;
190
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300191 // OSGi Properties
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700192 /**
193 * Add option 82 to relayed packets.
194 */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700195 protected boolean option82 = OPTION_82_DEFAULT;
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700196 /**
197 * Ask the DHCP Server to send back replies as L2 broadcast.
198 */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700199 protected boolean enableDhcpBroadcastReplies = ENABLE_DHCP_BROADCAST_REPLIES_DEFAULT;
Amit Ghosha17354e2017-08-23 12:56:04 +0100200
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800201 /**
202 * Number of threads used to process the packet.
203 */
204 protected int packetProcessorThreads = PACKET_PROCESSOR_THREADS_DEFAULT;
205
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300206 ScheduledFuture<?> refreshTask;
207 ScheduledExecutorService refreshService = Executors.newSingleThreadScheduledExecutor();
208
Amit Ghosh47243cb2017-07-26 05:08:53 +0100209 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
210 new DhcpRelayPacketProcessor();
211
Amit Ghosh8951f042017-08-10 13:48:10 +0100212 private InnerMastershipListener changeListener = new InnerMastershipListener();
213 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100214
Amit Ghosh8951f042017-08-10 13:48:10 +0100215 // connect points to the DHCP server
216 Set<ConnectPoint> dhcpConnectPoints;
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300217 protected AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100218 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
219 private ApplicationId appId;
220
Jonathan Hart617bc3e2020-02-14 10:42:23 -0800221 private ConsistentMap<String, DhcpAllocationInfo> allocations;
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300222 protected boolean modifyClientPktsSrcDstMac = false;
Amit Ghosh83c8c892017-11-09 11:08:27 +0000223 //Whether to use the uplink port of the OLTs to send/receive messages to the DHCP server
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300224 protected boolean useOltUplink = false;
Amit Ghosha17354e2017-08-23 12:56:04 +0100225
Gamze Abakac806c6c2018-12-03 12:49:46 +0000226 private BaseInformationService<SubscriberAndDeviceInformation> subsService;
227
Jonathan Hart77ca3152020-02-21 14:31:21 -0800228 private DhcpL2RelayStoreDelegate delegate = new InnerDhcpL2RelayStoreDelegate();
229
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800230 protected ExecutorService packetProcessorExecutor;
231
Amit Ghosh47243cb2017-07-26 05:08:53 +0100232 @Activate
233 protected void activate(ComponentContext context) {
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800234
Amit Ghosh47243cb2017-07-26 05:08:53 +0100235 //start the dhcp relay agent
236 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700237 // ensure that host-learning via dhcp includes IP addresses
238 componentConfigService.preSetProperty(HOST_LOC_PROVIDER,
239 "useDhcp", Boolean.TRUE.toString());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100240 componentConfigService.registerProperties(getClass());
Jonathan Hartc36c9552018-07-31 15:07:53 -0400241 eventDispatcher.addSink(DhcpL2RelayEvent.class, listenerRegistry);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100242
Jonathan Hart617bc3e2020-02-14 10:42:23 -0800243 KryoNamespace serializer = KryoNamespace.newBuilder()
244 .register(KryoNamespaces.API)
245 .register(Instant.class)
246 .register(DHCP.MsgType.class)
247 .register(DhcpAllocationInfo.class)
248 .build();
249
250 allocations = storageService.<String, DhcpAllocationInfo>consistentMapBuilder()
251 .withName("dhcpl2relay-allocations")
252 .withSerializer(Serializer.using(serializer))
253 .withApplicationId(appId)
254 .build();
255
Jonathan Hart77ca3152020-02-21 14:31:21 -0800256 dhcpL2RelayCounters.setDelegate(delegate);
257
Amit Ghosh47243cb2017-07-26 05:08:53 +0100258 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100259 mastershipService.addListener(changeListener);
260 deviceService.addListener(deviceListener);
261
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700262 subsService = sadisService.getSubscriberInfoService();
263
Amit Ghosh47243cb2017-07-26 05:08:53 +0100264 factories.forEach(cfgService::registerConfigFactory);
265 //update the dhcp server configuration.
266 updateConfig();
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800267
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000268 if (context != null) {
269 modified(context);
270 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100271
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800272 //add the packet services.
273 packetService.addProcessor(dhcpRelayPacketProcessor,
274 PacketProcessor.director(0));
275
Amit Ghosh47243cb2017-07-26 05:08:53 +0100276 log.info("DHCP-L2-RELAY Started");
277 }
278
279 @Deactivate
280 protected void deactivate() {
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300281 if (refreshTask != null) {
282 refreshTask.cancel(true);
283 }
284 if (refreshService != null) {
285 refreshService.shutdownNow();
286 }
Jonathan Hart77ca3152020-02-21 14:31:21 -0800287 dhcpL2RelayCounters.unsetDelegate(delegate);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100288 cfgService.removeListener(cfgListener);
289 factories.forEach(cfgService::unregisterConfigFactory);
290 packetService.removeProcessor(dhcpRelayPacketProcessor);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700291 cancelDhcpPktsFromServer();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100292
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800293 packetProcessorExecutor.shutdown();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100294 componentConfigService.unregisterProperties(getClass(), false);
Deepa Vaddireddy77a6ac72017-09-20 20:36:52 +0530295 deviceService.removeListener(deviceListener);
296 mastershipService.removeListener(changeListener);
Jonathan Hartc36c9552018-07-31 15:07:53 -0400297 eventDispatcher.removeSink(DhcpL2RelayEvent.class);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100298 log.info("DHCP-L2-RELAY Stopped");
299 }
300
301 @Modified
302 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000303
Amit Ghosh47243cb2017-07-26 05:08:53 +0100304 Dictionary<?, ?> properties = context.getProperties();
305
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700306 Boolean o = Tools.isPropertyEnabled(properties, OPTION_82);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100307 if (o != null) {
308 option82 = o;
309 }
Amit Ghosh2095dc62017-09-25 20:56:55 +0100310
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700311 o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_BROADCAST_REPLIES);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100312 if (o != null) {
313 enableDhcpBroadcastReplies = o;
314 }
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800315
316 String s = Tools.get(properties, PACKET_PROCESSOR_THREADS);
317 int oldpacketProcessorThreads = packetProcessorThreads;
318 packetProcessorThreads = Strings.isNullOrEmpty(s) ? oldpacketProcessorThreads
319 : Integer.parseInt(s.trim());
320 if (packetProcessorExecutor == null || oldpacketProcessorThreads != packetProcessorThreads) {
321 if (packetProcessorExecutor != null) {
322 packetProcessorExecutor.shutdown();
323 }
324 packetProcessorExecutor = newFixedThreadPool(packetProcessorThreads,
325 groupedThreads("onos/dhcp",
326 "dhcp-packet-%d", log));
327 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300328 }
329
Jonathan Hart617bc3e2020-02-14 10:42:23 -0800330 @Override
331 public Map<String, DhcpAllocationInfo> getAllocationInfo() {
332 return ImmutableMap.copyOf(allocations.asJavaMap());
333 }
334
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300335 /**
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700336 * Generates a unique UUID from a string.
337 *
338 * @return true if all information we need have been initialized
339 */
340 private static String getUniqueUuidFromString(String value) {
341 return UUID.nameUUIDFromBytes(value.getBytes()).toString();
342 }
343
344 /**
Amit Ghosh47243cb2017-07-26 05:08:53 +0100345 * Checks if this app has been configured.
346 *
347 * @return true if all information we need have been initialized
348 */
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300349 protected boolean configured() {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000350 if (!useOltUplink) {
351 return dhcpServerConnectPoint.get() != null;
352 }
353 return true;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100354 }
355
Amit Ghosh8951f042017-08-10 13:48:10 +0100356 /**
357 * Selects a connect point through an available device for which it is the master.
358 */
359 private void selectServerConnectPoint() {
360 synchronized (this) {
361 dhcpServerConnectPoint.set(null);
362 if (dhcpConnectPoints != null) {
363 // find a connect point through a device for which we are master
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700364 for (ConnectPoint cp : dhcpConnectPoints) {
Andrea Campanella6f45a1b2020-05-08 17:50:12 +0200365 if (isLocalLeader(cp.deviceId())) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100366 if (deviceService.isAvailable(cp.deviceId())) {
367 dhcpServerConnectPoint.set(cp);
368 }
369 log.info("DHCP connectPoint selected is {}", cp);
370 break;
371 }
372 }
373 }
374
375 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
376
377 if (dhcpServerConnectPoint.get() == null) {
378 log.error("Master of none, can't relay DHCP Message to server");
379 }
380 }
381 }
382
383 /**
384 * Updates the network configuration.
385 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100386 private void updateConfig() {
387 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
388 if (cfg == null) {
389 log.warn("Dhcp Server info not available");
390 return;
391 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100392
393 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
Amit Ghosh83c8c892017-11-09 11:08:27 +0000394 modifyClientPktsSrcDstMac = cfg.getModifySrcDstMacAddresses();
Saurav Dasb4e3e102018-10-02 15:31:17 -0700395 boolean prevUseOltUplink = useOltUplink;
Amit Ghosh83c8c892017-11-09 11:08:27 +0000396 useOltUplink = cfg.getUseOltUplinkForServerPktInOut();
Amit Ghosh8951f042017-08-10 13:48:10 +0100397
Saurav Dasb4e3e102018-10-02 15:31:17 -0700398 if (useOltUplink) {
399 for (ConnectPoint cp : getUplinkPortsOfOlts()) {
400 log.debug("requestDhcpPackets: ConnectPoint: {}", cp);
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700401 requestDhcpPacketsFromConnectPoint(cp, Optional.ofNullable(null));
Saurav Dasb4e3e102018-10-02 15:31:17 -0700402 }
403 // check if previous config was different and so trap flows may
Saurav Dasb14f08a2019-02-22 16:34:15 -0800404 // need to be removed from other places like AGG switches
Saurav Dasb4e3e102018-10-02 15:31:17 -0700405 if (!prevUseOltUplink) {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800406 addOrRemoveDhcpTrapFromServer(false);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700407 }
Saurav Dasb4e3e102018-10-02 15:31:17 -0700408 } else {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800409 // uplink on AGG switch
410 addOrRemoveDhcpTrapFromServer(true);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700411 }
412 }
413
414 private void cancelDhcpPktsFromServer() {
415 if (useOltUplink) {
416 for (ConnectPoint cp : getUplinkPortsOfOlts()) {
417 log.debug("cancelDhcpPackets: ConnectPoint: {}", cp);
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700418 cancelDhcpPacketsFromConnectPoint(cp, Optional.ofNullable(null));
Saurav Dasb4e3e102018-10-02 15:31:17 -0700419 }
420 } else {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800421 // uplink on AGG switch
422 addOrRemoveDhcpTrapFromServer(false);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000423 }
Saurav Dasb4e3e102018-10-02 15:31:17 -0700424 }
425
Saurav Dasb14f08a2019-02-22 16:34:15 -0800426 /**
427 * Used to add or remove DHCP trap flow for packets received from DHCP server.
428 * Typically used on a non OLT device, like an AGG switch. When adding, a
429 * new dhcp server connect point is selected from the configured options.
430 *
431 * @param add true if dhcp trap flow is to be added, false to remove the
432 * trap flow
433 */
434 private void addOrRemoveDhcpTrapFromServer(boolean add) {
435 if (add) {
436 selectServerConnectPoint();
437 log.debug("dhcp server connect point: " + dhcpServerConnectPoint);
438 }
439 if (dhcpServerConnectPoint.get() == null) {
440 log.warn("No dhcpServer connectPoint found, cannot {} dhcp trap flows",
441 (add) ? "install" : "remove");
442 return;
443 }
444 if (add) {
445 log.info("Adding trap to dhcp server connect point: "
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700446 + dhcpServerConnectPoint);
Saurav Dasb14f08a2019-02-22 16:34:15 -0800447 requestDhcpPacketsFromConnectPoint(dhcpServerConnectPoint.get(),
448 Optional.of(PacketPriority.HIGH1));
449 } else {
450 log.info("Removing trap from dhcp server connect point: "
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700451 + dhcpServerConnectPoint);
Saurav Dasb14f08a2019-02-22 16:34:15 -0800452 cancelDhcpPacketsFromConnectPoint(dhcpServerConnectPoint.get(),
453 Optional.of(PacketPriority.HIGH1));
454 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100455 }
456
457 /**
Amit Ghosh83c8c892017-11-09 11:08:27 +0000458 * Returns all the uplink ports of OLTs configured in SADIS.
459 * Only ports visible in ONOS and for which this instance is master
460 * are returned
461 */
462 private List<ConnectPoint> getUplinkPortsOfOlts() {
463 List<ConnectPoint> cps = new ArrayList<>();
464
465 // find all the olt devices and if their uplink ports are visible
466 Iterable<Device> devices = deviceService.getDevices();
467 for (Device d : devices) {
468 // check if this device is provisioned in Sadis
469
470 log.debug("getUplinkPortsOfOlts: Checking mastership of {}", d);
471 // do only for devices for which we are the master
Andrea Campanella6f45a1b2020-05-08 17:50:12 +0200472 if (!isLocalLeader(d.id())) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000473 continue;
474 }
475
476 String devSerialNo = d.serialNumber();
477 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
478 log.debug("getUplinkPortsOfOlts: Found device: {}", deviceInfo);
479 if (deviceInfo != null) {
480 // check if the uplink port with that number is available on the device
481 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
482 Port port = deviceService.getPort(d.id(), pNum);
483 log.debug("getUplinkPortsOfOlts: Found port: {}", port);
484 if (port != null) {
485 cps.add(new ConnectPoint(d.id(), pNum));
486 }
487 }
488 }
489 return cps;
490 }
491
492 /**
493 * Returns whether the passed port is the uplink port of the olt device.
494 */
495 private boolean isUplinkPortOfOlt(DeviceId dId, Port p) {
496 log.debug("isUplinkPortOfOlt: DeviceId: {} Port: {}", dId, p);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000497
498 Device d = deviceService.getDevice(dId);
499 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
500
501 if (deviceInfo != null) {
502 return (deviceInfo.uplinkPort() == p.number().toLong());
503 }
504
505 return false;
506 }
507
508 /**
509 * Returns the connectPoint which is the uplink port of the OLT.
510 */
511 private ConnectPoint getUplinkConnectPointOfOlt(DeviceId dId) {
512
513 Device d = deviceService.getDevice(dId);
514 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
515 log.debug("getUplinkConnectPointOfOlt DeviceId: {} devInfo: {}", dId, deviceInfo);
516 if (deviceInfo != null) {
517 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
518 Port port = deviceService.getPort(d.id(), pNum);
519 if (port != null) {
520 return new ConnectPoint(d.id(), pNum);
521 }
522 }
523
524 return null;
525 }
526
527 /**
528 * Request DHCP packet from particular connect point via PacketService.
Saurav Dasb14f08a2019-02-22 16:34:15 -0800529 * Optionally provide a priority for the trap flow. If no such priority is
530 * provided, the default priority will be used.
531 *
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700532 * @param cp the connect point to trap dhcp packets from
Saurav Dasb14f08a2019-02-22 16:34:15 -0800533 * @param priority of the trap flow, null to use default priority
Amit Ghosh83c8c892017-11-09 11:08:27 +0000534 */
Saurav Dasb14f08a2019-02-22 16:34:15 -0800535 private void requestDhcpPacketsFromConnectPoint(ConnectPoint cp,
536 Optional<PacketPriority> priority) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000537 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
538 .matchEthType(Ethernet.TYPE_IPV4)
539 .matchInPort(cp.port())
540 .matchIPProtocol(IPv4.PROTOCOL_UDP)
541 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
542 packetService.requestPackets(selectorServer.build(),
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700543 priority.isPresent() ? priority.get() : PacketPriority.CONTROL,
544 appId, Optional.of(cp.deviceId()));
Amit Ghosh83c8c892017-11-09 11:08:27 +0000545 }
546
547 /**
Saurav Dasb14f08a2019-02-22 16:34:15 -0800548 * Cancel DHCP packet from particular connect point via PacketService. If
549 * the request was made with a specific packet priority, then the same
550 * priority should be used in this call.
551 *
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700552 * @param cp the connect point for the trap flow
Saurav Dasb14f08a2019-02-22 16:34:15 -0800553 * @param priority with which the trap flow was requested; if request
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700554 * priority was not specified, this param should also be null
Amit Ghosh83c8c892017-11-09 11:08:27 +0000555 */
Saurav Dasb14f08a2019-02-22 16:34:15 -0800556 private void cancelDhcpPacketsFromConnectPoint(ConnectPoint cp,
557 Optional<PacketPriority> priority) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000558 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
559 .matchEthType(Ethernet.TYPE_IPV4)
560 .matchInPort(cp.port())
561 .matchIPProtocol(IPv4.PROTOCOL_UDP)
562 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
563 packetService.cancelPackets(selectorServer.build(),
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700564 priority.isPresent() ? priority.get() : PacketPriority.CONTROL,
565 appId, Optional.of(cp.deviceId()));
Amit Ghosh83c8c892017-11-09 11:08:27 +0000566 }
567
Amit Ghosh47243cb2017-07-26 05:08:53 +0100568 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
569 String serialNo = deviceService.getDevice(context.inPacket().
570 receivedFrom().deviceId()).serialNumber();
571
572 return subsService.get(serialNo);
573 }
574
Amit Ghosh47243cb2017-07-26 05:08:53 +0100575 private MacAddress relayAgentMacAddress(PacketContext context) {
576
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000577 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100578 if (device == null) {
579 log.warn("Device not found for {}", context.inPacket().
580 receivedFrom());
581 return null;
582 }
583
584 return device.hardwareIdentifier();
585 }
586
587 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100588 return nasPortId(context.inPacket().receivedFrom());
589 }
590
591 private String nasPortId(ConnectPoint cp) {
592 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100593 return p.annotations().value(AnnotationKeys.PORT_NAME);
594 }
595
596 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100597 return subsService.get(nasPortId(context));
598 }
599
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000600 private UniTagInformation getUnitagInformationFromPacketContext(PacketContext context,
601 SubscriberAndDeviceInformation sub) {
602 //If the ctag is defined in the tagList and dhcp is required, return the service info
603 List<UniTagInformation> tagList = sub.uniTagList();
604 for (UniTagInformation uniServiceInformation : tagList) {
605 if (uniServiceInformation.getPonCTag().toShort() == context.inPacket().parsed().getVlanID()) {
606 if (uniServiceInformation.getIsDhcpRequired()) {
607 return uniServiceInformation;
608 }
609 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100610 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100611
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000612 return null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100613 }
614
615 private class DhcpRelayPacketProcessor implements PacketProcessor {
616
617 @Override
618 public void process(PacketContext context) {
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800619 packetProcessorExecutor.execute(() -> {
620 processInternal(context);
621 });
622 }
623
624 private void processInternal(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100625 if (!configured()) {
626 log.warn("Missing DHCP relay config. Abort packet processing");
627 return;
628 }
629
630 // process the packet and get the payload
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530631 Ethernet packet = context.inPacket().parsed();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000632
Amit Ghosh47243cb2017-07-26 05:08:53 +0100633 if (packet == null) {
634 log.warn("Packet is null");
635 return;
636 }
637
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530638 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100639 IPv4 ipv4Packet = (IPv4) packet.getPayload();
640
641 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
642 UDP udpPacket = (UDP) ipv4Packet.getPayload();
643 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000644 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100645 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800646 if (log.isTraceEnabled()) {
647 log.trace("Processing packet with type {} from MAC {}",
648 getDhcpPacketType(dhcpPayload),
649 MacAddress.valueOf(dhcpPayload.getClientHardwareAddress()));
650 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100651 //This packet is dhcp.
652 processDhcpPacket(context, packet, dhcpPayload);
653 }
654 }
655 }
656 }
657
658 //forward the packet to ConnectPoint where the DHCP server is attached.
Amit Ghosh83c8c892017-11-09 11:08:27 +0000659 private void forwardPacket(Ethernet packet, PacketContext context) {
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800660 if (log.isTraceEnabled()) {
661 IPv4 ipv4Packet = (IPv4) packet.getPayload();
662 UDP udpPacket = (UDP) ipv4Packet.getPayload();
663 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
664 log.trace("Emitting packet to server: packet {}, with MAC {}",
665 getDhcpPacketType(dhcpPayload),
666 MacAddress.valueOf(dhcpPayload.getClientHardwareAddress()));
667 }
Amit Ghosh83c8c892017-11-09 11:08:27 +0000668 ConnectPoint toSendTo = null;
Amit Ghosh83c8c892017-11-09 11:08:27 +0000669 if (!useOltUplink) {
670 toSendTo = dhcpServerConnectPoint.get();
671 } else {
672 toSendTo = getUplinkConnectPointOfOlt(context.inPacket().
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700673 receivedFrom().deviceId());
Amit Ghosh83c8c892017-11-09 11:08:27 +0000674 }
675
676 if (toSendTo != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100677 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh83c8c892017-11-09 11:08:27 +0000678 .setOutput(toSendTo.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100679 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh83c8c892017-11-09 11:08:27 +0000680 toSendTo.deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100681 ByteBuffer.wrap(packet.serialize()));
682 if (log.isTraceEnabled()) {
Saurav Das15626a02018-09-27 18:36:45 -0700683 log.trace("Relaying packet to dhcp server at {} {}",
684 toSendTo, packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100685 }
686 packetService.emit(o);
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300687
688 SubscriberAndDeviceInformation entry = getSubscriberInfoFromClient(context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800689 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("PACKETS_TO_SERVER"));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100690 } else {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000691 log.error("No connect point to send msg to DHCP Server");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100692 }
693 }
694
Amit Ghosha17354e2017-08-23 12:56:04 +0100695 // get the type of the DHCP packet
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700696 private DHCP.MsgType getDhcpPacketType(DHCP dhcpPayload) {
Amit Ghosha17354e2017-08-23 12:56:04 +0100697
Jonathan Hartedbf6422018-05-02 17:30:05 -0700698 for (DhcpOption option : dhcpPayload.getOptions()) {
Amit Ghosha17354e2017-08-23 12:56:04 +0100699 if (option.getCode() == OptionCode_MessageType.getValue()) {
700 byte[] data = option.getData();
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700701 return DHCP.MsgType.getType(data[0]);
Amit Ghosha17354e2017-08-23 12:56:04 +0100702 }
703 }
704 return null;
705 }
706
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700707 private void updateDhcpRelayCountersStore(SubscriberAndDeviceInformation entry,
708 DhcpL2RelayCounterNames counterType) {
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300709 // Update global counter stats
710 dhcpL2RelayCounters.incrementCounter(DhcpL2RelayEvent.GLOBAL_COUNTER, counterType);
711 if (entry == null) {
712 log.warn("Counter not updated as subscriber info not found.");
713 } else {
714 // Update subscriber counter stats
715 dhcpL2RelayCounters.incrementCounter(entry.id(), counterType);
716 }
717 }
718
719 /*
720 * Get subscriber information based on it's context packet.
721 */
722 private SubscriberAndDeviceInformation getSubscriberInfoFromClient(PacketContext context) {
723 if (context != null) {
724 return getSubscriber(context);
725 }
726 return null;
727 }
728
729 /*
730 * Get subscriber information based on it's DHCP payload.
731 */
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000732 private SubscriberAndDeviceInformation getSubscriberInfoFromServer(DHCP dhcpPayload, PacketContext context) {
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300733 if (dhcpPayload != null) {
734 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000735 ConnectPoint subsCp = getConnectPointOfClient(descMac, context);
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300736
737 if (subsCp != null) {
738 String portId = nasPortId(subsCp);
739 return subsService.get(portId);
740 }
741 }
742 return null;
743 }
744
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700745 // process the dhcp packet before relaying to server or client
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530746 private void processDhcpPacket(PacketContext context, Ethernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100747 DHCP dhcpPayload) {
748 if (dhcpPayload == null) {
749 log.warn("DHCP payload is null");
750 return;
751 }
752
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700753 DHCP.MsgType incomingPacketType = getDhcpPacketType(dhcpPayload);
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300754 if (incomingPacketType == null) {
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700755 log.warn("DHCP Packet type not found. Dump of ethernet pkt in hex format for troubleshooting.");
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300756 byte[] array = packet.serialize();
757 ByteArrayOutputStream buf = new ByteArrayOutputStream();
758 try {
759 HexDump.dump(array, 0, buf, 0);
760 log.trace(buf.toString());
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700761 } catch (Exception e) {
762 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300763 return;
764 }
765
766 SubscriberAndDeviceInformation entry = null;
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000767
Matteo Scandoloeb5a0dc2020-09-15 14:54:28 -0700768 MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
769
770 log.info("Received DHCP Packet of type {} from {} with Client MacAddress {} and vlan {}",
771 incomingPacketType, context.inPacket().receivedFrom(),
772 clientMacAddress, packet.getVlanID());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100773
774 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000775 case DHCPDISCOVER:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530776 Ethernet ethernetPacketDiscover =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000777 processDhcpPacketFromClient(context, packet);
778 if (ethernetPacketDiscover != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000779 forwardPacket(ethernetPacketDiscover, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000780 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300781 entry = getSubscriberInfoFromClient(context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800782 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("DHCPDISCOVER"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000783 break;
784 case DHCPOFFER:
785 //reply to dhcp client.
Saurav Das15626a02018-09-27 18:36:45 -0700786 Ethernet ethernetPacketOffer =
787 processDhcpPacketFromServer(context, packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000788 if (ethernetPacketOffer != null) {
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000789 sendReply(ethernetPacketOffer, dhcpPayload, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000790 }
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000791 entry = getSubscriberInfoFromServer(dhcpPayload, context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800792 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("DHCPOFFER"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000793 break;
794 case DHCPREQUEST:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530795 Ethernet ethernetPacketRequest =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000796 processDhcpPacketFromClient(context, packet);
797 if (ethernetPacketRequest != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000798 forwardPacket(ethernetPacketRequest, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000799 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300800 entry = getSubscriberInfoFromClient(context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800801 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("DHCPREQUEST"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000802 break;
803 case DHCPACK:
804 //reply to dhcp client.
Saurav Das15626a02018-09-27 18:36:45 -0700805 Ethernet ethernetPacketAck =
806 processDhcpPacketFromServer(context, packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000807 if (ethernetPacketAck != null) {
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000808 sendReply(ethernetPacketAck, dhcpPayload, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000809 }
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000810 entry = getSubscriberInfoFromServer(dhcpPayload, context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800811 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("DHCPACK"));
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300812 break;
813 case DHCPDECLINE:
Arjun E K05ad20b2020-03-13 13:25:17 +0000814 Ethernet ethernetPacketDecline =
815 processDhcpPacketFromClient(context, packet);
816 if (ethernetPacketDecline != null) {
817 forwardPacket(ethernetPacketDecline, context);
818 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300819 entry = getSubscriberInfoFromClient(context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800820 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("DHCPDECLINE"));
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300821 break;
822 case DHCPNAK:
Arjun E K05ad20b2020-03-13 13:25:17 +0000823 //reply to dhcp client.
824 Ethernet ethernetPacketNak =
825 processDhcpPacketFromServer(context, packet);
826 if (ethernetPacketNak != null) {
827 sendReply(ethernetPacketNak, dhcpPayload, context);
828 }
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000829 entry = getSubscriberInfoFromServer(dhcpPayload, context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800830 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("DHCPNACK"));
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300831 break;
832 case DHCPRELEASE:
Thomas Lee S0dc9a3b2020-01-14 10:42:29 +0530833 Ethernet ethernetPacketRelease =
834 processDhcpPacketFromClient(context, packet);
835 if (ethernetPacketRelease != null) {
836 forwardPacket(ethernetPacketRelease, context);
837 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300838 entry = getSubscriberInfoFromClient(context);
Jonathan Hart77ca3152020-02-21 14:31:21 -0800839 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("DHCPRELEASE"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000840 break;
841 default:
842 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100843 }
844 }
845
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530846 private Ethernet processDhcpPacketFromClient(PacketContext context,
847 Ethernet ethernetPacket) {
Saurav Das15626a02018-09-27 18:36:45 -0700848 if (log.isTraceEnabled()) {
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700849 log.trace("DHCP Packet received from client at {} {}",
Saurav Das15626a02018-09-27 18:36:45 -0700850 context.inPacket().receivedFrom(), ethernetPacket);
851 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100852
853 MacAddress relayAgentMac = relayAgentMacAddress(context);
854 if (relayAgentMac == null) {
855 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100856 return null;
857 }
858
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530859 Ethernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100860
861 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
862 UDP udpPacket = (UDP) ipv4Packet.getPayload();
863 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700864 ConnectPoint inPort = context.inPacket().receivedFrom();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100865
Amit Ghosha17354e2017-08-23 12:56:04 +0100866 if (enableDhcpBroadcastReplies) {
867 // We want the reply to come back as a L2 broadcast
868 dhcpPacket.setFlags((short) 0x8000);
869 }
870
Jonathan Hartc36c9552018-07-31 15:07:53 -0400871 MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700872 VlanId clientVlan = VlanId.vlanId(ethernetPacket.getVlanID());
Jonathan Hartc36c9552018-07-31 15:07:53 -0400873 IpAddress clientIp = IpAddress.valueOf(dhcpPacket.getClientIPAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100874
Jonathan Hartc36c9552018-07-31 15:07:53 -0400875 SubscriberAndDeviceInformation entry = getSubscriber(context);
876 if (entry == null) {
Saurav Das15626a02018-09-27 18:36:45 -0700877 log.warn("Dropping packet as subscriber entry is not available");
Jonathan Hartc36c9552018-07-31 15:07:53 -0400878 return null;
879 }
880
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000881 UniTagInformation uniTagInformation = getUnitagInformationFromPacketContext(context, entry);
882 if (uniTagInformation == null) {
883 log.warn("Missing service information for connectPoint {} / cTag {}",
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700884 inPort, clientVlan);
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000885 return null;
886 }
887
Jonathan Hartc36c9552018-07-31 15:07:53 -0400888 DhcpAllocationInfo info = new DhcpAllocationInfo(
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700889 inPort, dhcpPacket.getPacketType(), entry.circuitId(),
890 clientMac, clientIp, clientVlan, entry.id());
Jonathan Hartc36c9552018-07-31 15:07:53 -0400891
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700892 String key = getUniqueUuidFromString(entry.id() + clientMac
893 + clientVlan);
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700894 allocations.put(key, info);
Jonathan Hartc36c9552018-07-31 15:07:53 -0400895
Saurav Das15626a02018-09-27 18:36:45 -0700896 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.UPDATED, info,
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700897 inPort));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000898 if (option82) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000899 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
900 udpPacket.setPayload(dhcpPacketWithOption82);
901 }
902
903 ipv4Packet.setPayload(udpPacket);
904 etherReply.setPayload(ipv4Packet);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000905 if (modifyClientPktsSrcDstMac) {
906 etherReply.setSourceMACAddress(relayAgentMac);
907 etherReply.setDestinationMACAddress(dhcpConnectMac);
908 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100909
Amit Ghosh8951f042017-08-10 13:48:10 +0100910 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000911 etherReply.setVlanID(uniTagInformation.getPonCTag().toShort());
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530912 etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000913 etherReply.setQinQVID(uniTagInformation.getPonSTag().toShort());
914 if (uniTagInformation.getUsPonSTagPriority() != -1) {
915 etherReply.setQinQPriorityCode((byte) uniTagInformation.getUsPonSTagPriority());
916 }
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700917 log.info("Finished processing DHCP Packet of type {} from {} and relaying to dhcpServer",
918 dhcpPacket.getPacketType(), entry.id());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100919 return etherReply;
920 }
921
922 //build the DHCP offer/ack with proper client port.
Saurav Das15626a02018-09-27 18:36:45 -0700923 private Ethernet processDhcpPacketFromServer(PacketContext context,
924 Ethernet ethernetPacket) {
925 if (log.isTraceEnabled()) {
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700926 log.trace("DHCP Packet received from server at {} {}",
Saurav Das15626a02018-09-27 18:36:45 -0700927 context.inPacket().receivedFrom(), ethernetPacket);
928 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100929 // get dhcp header.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530930 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100931 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
932 UDP udpPacket = (UDP) ipv4Packet.getPayload();
933 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700934 VlanId innerVlan = VlanId.vlanId(ethernetPacket.getVlanID());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100935
Amit Ghosh47243cb2017-07-26 05:08:53 +0100936 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000937 ConnectPoint subsCp = getConnectPointOfClient(dstMac, context);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100938 // If we can't find the subscriber, can't process further
939 if (subsCp == null) {
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700940 log.warn("Couldn't find subscriber, service or host info for mac"
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -0800941 + " address {} and vlan {} .. DHCP packet won't be delivered", dstMac, innerVlan);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100942 return null;
943 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100944
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000945 SubscriberAndDeviceInformation entry = getSubscriberInfoFromServer(dhcpPayload, context);
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700946 if (entry != null) {
Thomas Lee S0dc9a3b2020-01-14 10:42:29 +0530947 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700948 // store DHCPAllocationInfo
Thomas Lee S0dc9a3b2020-01-14 10:42:29 +0530949 DhcpAllocationInfo info = new DhcpAllocationInfo(subsCp,
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700950 dhcpPayload.getPacketType(), entry.circuitId(), dstMac, ip,
951 innerVlan, entry.id());
952 String key = getUniqueUuidFromString(entry.id()
953 + info.macAddress() + innerVlan);
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700954 allocations.put(key, info);
Thomas Lee S0dc9a3b2020-01-14 10:42:29 +0530955
956 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.UPDATED, info, subsCp));
Saurav Dasbd5ce9c2020-09-04 18:46:45 -0700957 }
Thomas Lee S0dc9a3b2020-01-14 10:42:29 +0530958
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000959 UniTagInformation uniTagInformation = getUnitagInformationFromPacketContext(context, entry);
960 if (uniTagInformation == null) {
961 log.warn("Missing service information for connectPoint {} / cTag {}",
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700962 context.inPacket().receivedFrom(), context.inPacket().parsed().getVlanID());
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000963 return null;
964 }
965
Jonathan Hart77ca3152020-02-21 14:31:21 -0800966 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounterNames.valueOf("PACKETS_FROM_SERVER"));
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300967
Amit Ghosh47243cb2017-07-26 05:08:53 +0100968 // we leave the srcMac from the original packet
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000969 etherReply.setQinQVID(VlanId.NO_VID);
970 etherReply.setQinQPriorityCode((byte) 0);
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530971 etherReply.setDestinationMACAddress(dstMac);
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000972 etherReply.setVlanID(uniTagInformation.getPonCTag().toShort());
973 if (uniTagInformation.getUsPonCTagPriority() != -1) {
974 etherReply.setPriorityCode((byte) uniTagInformation.getUsPonCTagPriority());
975 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100976
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000977 if (option82) {
978 udpPacket.setPayload(removeOption82(dhcpPayload));
979 } else {
980 udpPacket.setPayload(dhcpPayload);
981 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100982 ipv4Packet.setPayload(udpPacket);
983 etherReply.setPayload(ipv4Packet);
984
Saurav Das15626a02018-09-27 18:36:45 -0700985 log.info("Finished processing packet.. relaying to client");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100986 return etherReply;
987 }
988
Amit Ghosha17354e2017-08-23 12:56:04 +0100989 /*
990 * Get ConnectPoint of the Client based on it's MAC address
991 */
Gamze Abakaa64b3bc2020-01-31 06:51:43 +0000992 private ConnectPoint getConnectPointOfClient(MacAddress dstMac, PacketContext context) {
Amit Ghosha17354e2017-08-23 12:56:04 +0100993 Set<Host> hosts = hostService.getHostsByMac(dstMac);
994 if (hosts == null || hosts.isEmpty()) {
995 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Matteo Scandolo64bba8c2020-08-19 11:50:33 -0700996 + "relay for DHCP Packet from server", dstMac);
Amit Ghosha17354e2017-08-23 12:56:04 +0100997 return null;
998 }
999 for (Host h : hosts) {
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001000 // if more than one (for example, multiple services with same
1001 // mac-address but different service VLANs (inner/C vlans)
Amit Ghosha17354e2017-08-23 12:56:04 +01001002 // find the connect point which has an valid entry in SADIS
1003 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001004 h.location().port());
Amit Ghosha17354e2017-08-23 12:56:04 +01001005
Gamze Abakaa64b3bc2020-01-31 06:51:43 +00001006 String portId = nasPortId(cp);
1007 SubscriberAndDeviceInformation sub = subsService.get(portId);
1008 if (sub == null) {
1009 log.warn("Subscriber info not found for {}", cp);
1010 return null;
Amit Ghosha17354e2017-08-23 12:56:04 +01001011 }
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001012 // check for cvlan in subscriber's uniTagInfo list
Gamze Abakaa64b3bc2020-01-31 06:51:43 +00001013 UniTagInformation uniTagInformation = getUnitagInformationFromPacketContext(context, sub);
Jonathan Hartb4fbc922020-04-14 12:17:44 -07001014 if (uniTagInformation != null) {
1015 return cp;
Gamze Abakaa64b3bc2020-01-31 06:51:43 +00001016 }
Gamze Abakaa64b3bc2020-01-31 06:51:43 +00001017 }
Jonathan Hartb4fbc922020-04-14 12:17:44 -07001018 // no sadis config found for this connectPoint/vlan
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001019 log.warn("Missing service information for dhcp packet received from"
1020 + " {} with cTag {} .. cannot relay to client",
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001021 context.inPacket().receivedFrom(), context.inPacket().parsed().getVlanID());
Jonathan Hartb4fbc922020-04-14 12:17:44 -07001022
Amit Ghosha17354e2017-08-23 12:56:04 +01001023 return null;
1024 }
1025
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001026 // send the response to the requester host (client)
Gamze Abakaa64b3bc2020-01-31 06:51:43 +00001027 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload, PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +01001028 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Gamze Abakaa64b3bc2020-01-31 06:51:43 +00001029 ConnectPoint subCp = getConnectPointOfClient(descMac, context);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001030 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +01001031 if (subCp != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +01001032 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +01001033 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +01001034 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +01001035 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +01001036 if (log.isTraceEnabled()) {
Matteo Scandoloa8b6eea2018-11-27 13:47:58 -08001037 log.trace("Relaying packet to DHCP client at {} with MacAddress {}, {}", subCp,
1038 descMac, ethPacket);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001039 }
1040 packetService.emit(o);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001041 } else {
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001042 log.error("Dropping DHCP Packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001043 }
1044 }
1045 }
1046
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001047 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001048 log.trace("option82data {} ", entry);
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001049
Jonathan Hartedbf6422018-05-02 17:30:05 -07001050 List<DhcpOption> options = Lists.newArrayList(dhcpPacket.getOptions());
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001051 DhcpOption82 option82 = new DhcpOption82();
1052 option82.setAgentCircuitId(entry.circuitId());
1053 option82.setAgentRemoteId(entry.remoteId());
Jonathan Hartedbf6422018-05-02 17:30:05 -07001054 DhcpOption option = new DhcpOption()
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +05301055 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
1056 .setData(option82.toByteArray())
1057 .setLength(option82.length());
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001058
1059 options.add(options.size() - 1, option);
1060 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +01001061
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001062 return dhcpPacket;
1063
1064 }
1065
1066 private DHCP removeOption82(DHCP dhcpPacket) {
Jonathan Hartedbf6422018-05-02 17:30:05 -07001067 List<DhcpOption> options = dhcpPacket.getOptions();
1068 List<DhcpOption> newoptions = options.stream()
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001069 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
1070 .collect(Collectors.toList());
1071
1072 return dhcpPacket.setOptions(newoptions);
1073 }
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001074
Amit Ghosh47243cb2017-07-26 05:08:53 +01001075 /**
1076 * Listener for network config events.
1077 */
1078 private class InternalConfigListener implements NetworkConfigListener {
1079
1080 @Override
1081 public void event(NetworkConfigEvent event) {
1082
1083 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
1084 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
1085 event.configClass().equals(DhcpL2RelayConfig.class)) {
1086 updateConfig();
1087 log.info("Reconfigured");
1088 }
1089 }
1090 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001091
Amit Ghosh8951f042017-08-10 13:48:10 +01001092 /**
1093 * Handles Mastership changes for the devices which connect
1094 * to the DHCP server.
1095 */
1096 private class InnerMastershipListener implements MastershipListener {
1097 @Override
1098 public void event(MastershipEvent event) {
Amit Ghosh83c8c892017-11-09 11:08:27 +00001099 if (!useOltUplink) {
1100 if (dhcpServerConnectPoint.get() != null &&
1101 dhcpServerConnectPoint.get().deviceId().
1102 equals(event.subject())) {
1103 log.trace("Mastership Event recevived for {}", event.subject());
1104 // mastership of the device for our connect point has changed
1105 // reselect
1106 selectServerConnectPoint();
1107 }
Amit Ghosh8951f042017-08-10 13:48:10 +01001108 }
1109 }
1110 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001111
Jonathan Hart617bc3e2020-02-14 10:42:23 -08001112 private void removeAllocations(Predicate<Map.Entry<String, Versioned<DhcpAllocationInfo>>> pred) {
1113 allocations.stream()
1114 .filter(pred)
1115 .map(Map.Entry::getKey)
1116 .collect(Collectors.toList())
1117 .forEach(allocations::remove);
1118 }
1119
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001120 @Override
Matteo Scandoloab346512020-04-17 13:39:55 -07001121 public void clearAllocations() {
1122 allocations.clear();
1123 }
1124
1125
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001126 @Override
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001127 public boolean removeAllocationsByConnectPoint(ConnectPoint cp) {
1128 boolean removed = false;
Matteo Scandoloab346512020-04-17 13:39:55 -07001129 for (String key : allocations.keySet()) {
1130 DhcpAllocationInfo entry = allocations.asJavaMap().get(key);
1131 if (entry.location().equals(cp)) {
1132 allocations.remove(key);
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001133 removed = true;
Matteo Scandoloab346512020-04-17 13:39:55 -07001134 }
1135 }
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001136 return removed;
Matteo Scandoloab346512020-04-17 13:39:55 -07001137 }
1138
Andrea Campanella6f45a1b2020-05-08 17:50:12 +02001139
1140 /**
1141 * Checks for mastership or falls back to leadership on deviceId.
1142 * If the node is not master and device is available
1143 * or the device is not available and the leader is different
1144 * we let master or leader else handle it
1145 * Leadership on the device topic is needed because the master can be NONE
1146 * in case the device went away, we still need to handle events
1147 * consistently
1148 */
1149 private boolean isLocalLeader(DeviceId deviceId) {
1150 if (!mastershipService.isLocalMaster(deviceId)) {
1151 // When the device is available we just check the mastership
1152 if (deviceService.isAvailable(deviceId)) {
1153 return false;
1154 }
1155 // Fallback with Leadership service - device id is used as topic
1156 NodeId leader = leadershipService.runForLeadership(
1157 deviceId.toString()).leaderNodeId();
1158 // Verify if this node is the leader
1159 return clusterService.getLocalNode().id().equals(leader);
1160 }
1161 return true;
1162 }
1163
Amit Ghosh8951f042017-08-10 13:48:10 +01001164 /**
1165 * Handles Device status change for the devices which connect
1166 * to the DHCP server.
1167 */
1168 private class InnerDeviceListener implements DeviceListener {
1169 @Override
1170 public void event(DeviceEvent event) {
Andrea Campanella6f45a1b2020-05-08 17:50:12 +02001171 final DeviceId deviceId = event.subject().id();
1172
1173 // Ensure only one instance handles the event
1174 if (!isLocalLeader(deviceId)) {
Jonathan Hart617bc3e2020-02-14 10:42:23 -08001175 return;
1176 }
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001177 // ignore stats
1178 if (event.type().equals(DeviceEvent.Type.PORT_STATS_UPDATED)) {
1179 return;
1180 }
Jonathan Hart617bc3e2020-02-14 10:42:23 -08001181
Saurav Dasbd5ce9c2020-09-04 18:46:45 -07001182 log.debug("Device Event received for {} event {}", event.subject(),
1183 event.type());
Jonathan Hart617bc3e2020-02-14 10:42:23 -08001184
Thomas Lee S9df15082019-12-23 11:31:15 +05301185 switch (event.type()) {
Jonathan Hart617bc3e2020-02-14 10:42:23 -08001186 case DEVICE_REMOVED:
1187 log.info("Device removed {}", event.subject().id());
1188 removeAllocations(e -> e.getValue().value().location().deviceId().equals(deviceId));
1189 break;
Thomas Lee S9df15082019-12-23 11:31:15 +05301190 case DEVICE_AVAILABILITY_CHANGED:
Jonathan Hart617bc3e2020-02-14 10:42:23 -08001191 boolean available = deviceService.isAvailable(deviceId);
1192 log.info("Device Avail Changed {} to {}", event.subject().id(), available);
1193
1194 if (!available && deviceService.getPorts(deviceId).isEmpty()) {
1195 removeAllocations(e -> e.getValue().value().location().deviceId().equals(deviceId));
1196 log.info("Device {} is removed from DHCP allocationmap ", deviceId);
Thomas Lee S9df15082019-12-23 11:31:15 +05301197 }
1198 break;
Thomas Lee S6b77ad22020-01-10 11:27:43 +05301199 case PORT_REMOVED:
1200 Port port = event.port();
Thomas Lee S6b77ad22020-01-10 11:27:43 +05301201 log.info("Port {} is deleted on device {}", port, deviceId);
Jonathan Hart617bc3e2020-02-14 10:42:23 -08001202
1203 ConnectPoint cp = new ConnectPoint(deviceId, port.number());
1204 removeAllocations(e -> e.getValue().value().location().equals(cp));
1205
Thomas Lee S6b77ad22020-01-10 11:27:43 +05301206 log.info("Port {} on device {} is removed from DHCP allocationmap", event.port(), deviceId);
1207 break;
Thomas Lee S9df15082019-12-23 11:31:15 +05301208 default:
1209 break;
1210 }
Amit Ghosh83c8c892017-11-09 11:08:27 +00001211 if (!useOltUplink) {
1212 if (dhcpServerConnectPoint.get() == null) {
1213 switch (event.type()) {
1214 case DEVICE_ADDED:
1215 case DEVICE_AVAILABILITY_CHANGED:
Saurav Dasb14f08a2019-02-22 16:34:15 -08001216 // some device is available check if we can get a
1217 // connect point we can use
1218 addOrRemoveDhcpTrapFromServer(true);
Amit Ghosh83c8c892017-11-09 11:08:27 +00001219 break;
1220 default:
1221 break;
1222 }
1223 return;
Amit Ghosh8951f042017-08-10 13:48:10 +01001224 }
Amit Ghosh83c8c892017-11-09 11:08:27 +00001225 if (dhcpServerConnectPoint.get().deviceId().
1226 equals(event.subject().id())) {
1227 switch (event.type()) {
1228 case DEVICE_AVAILABILITY_CHANGED:
1229 case DEVICE_REMOVED:
1230 case DEVICE_SUSPENDED:
1231 // state of our device has changed, check if we need
Saurav Dasb14f08a2019-02-22 16:34:15 -08001232 // to re-select a connectpoint
1233 addOrRemoveDhcpTrapFromServer(true);
Amit Ghosh83c8c892017-11-09 11:08:27 +00001234 break;
1235 default:
1236 break;
1237 }
1238 }
1239 } else {
Amit Ghosh8951f042017-08-10 13:48:10 +01001240 switch (event.type()) {
Amit Ghosh83c8c892017-11-09 11:08:27 +00001241 case PORT_ADDED:
Saurav Dasb4e3e102018-10-02 15:31:17 -07001242 if (useOltUplink && isUplinkPortOfOlt(event.subject().id(), event.port())) {
Saurav Dasb14f08a2019-02-22 16:34:15 -08001243 requestDhcpPacketsFromConnectPoint(
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001244 new ConnectPoint(event.subject().id(), event.port().number()),
1245 Optional.empty());
Amit Ghosh83c8c892017-11-09 11:08:27 +00001246 }
Amit Ghosh8951f042017-08-10 13:48:10 +01001247 break;
1248 default:
1249 break;
1250 }
1251 }
1252 }
1253 }
Jonathan Hart77ca3152020-02-21 14:31:21 -08001254
1255 private class InnerDhcpL2RelayStoreDelegate implements DhcpL2RelayStoreDelegate {
1256 @Override
1257 public void notify(DhcpL2RelayEvent event) {
1258 if (event.type().equals(DhcpL2RelayEvent.Type.STATS_UPDATE)) {
1259 DhcpL2RelayEvent toPost = event;
1260 if (event.getSubscriberId() != null) {
1261 // infuse the event with the allocation info before posting
1262 DhcpAllocationInfo info = Versioned.valueOrNull(allocations.get(event.getSubscriberId()));
1263 toPost = new DhcpL2RelayEvent(event.type(), info, event.connectPoint(),
Matteo Scandolo64bba8c2020-08-19 11:50:33 -07001264 event.getCountersEntry(), event.getSubscriberId());
Jonathan Hart77ca3152020-02-21 14:31:21 -08001265 }
1266 post(toPost);
1267 }
1268
1269 }
1270 }
Amit Ghosh47243cb2017-07-26 05:08:53 +01001271}