blob: 2cd4f88899e2fed0fe442bd656c0b98f5c97489d [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;
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -030022import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
23import static org.onlab.packet.MacAddress.valueOf;
24import static org.onlab.util.Tools.get;
25import static org.onlab.util.Tools.getIntegerProperty;
26import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
27
28import java.io.ByteArrayOutputStream;
29import java.nio.ByteBuffer;
30import java.util.AbstractMap;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.Dictionary;
34import java.util.List;
35import java.util.Map;
36import java.util.Optional;
37import java.util.Set;
38import java.util.concurrent.Executors;
39import java.util.concurrent.atomic.AtomicLong;
40import java.util.concurrent.atomic.AtomicReference;
41import java.util.concurrent.ScheduledExecutorService;
42import java.util.concurrent.ScheduledFuture;
43import java.util.concurrent.TimeUnit;
44import java.util.stream.Collectors;
45
46import org.apache.commons.io.HexDump;
Amit Ghosh47243cb2017-07-26 05:08:53 +010047import org.onlab.packet.DHCP;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000048import org.onlab.packet.Ethernet;
Amit Ghosh47243cb2017-07-26 05:08:53 +010049import org.onlab.packet.IPv4;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053050import org.onlab.packet.IpAddress;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000051import org.onlab.packet.MacAddress;
Amit Ghosh47243cb2017-07-26 05:08:53 +010052import org.onlab.packet.TpPort;
53import org.onlab.packet.UDP;
54import org.onlab.packet.VlanId;
Jonathan Hartedbf6422018-05-02 17:30:05 -070055import org.onlab.packet.dhcp.DhcpOption;
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -030056import org.onlab.util.SafeRecurringTask;
Amit Ghosh47243cb2017-07-26 05:08:53 +010057import org.onlab.util.Tools;
58import org.onosproject.cfg.ComponentConfigService;
59import org.onosproject.core.ApplicationId;
60import org.onosproject.core.CoreService;
Jonathan Hartc36c9552018-07-31 15:07:53 -040061import org.onosproject.event.AbstractListenerManager;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053062import org.onosproject.mastership.MastershipEvent;
63import org.onosproject.mastership.MastershipListener;
64import org.onosproject.mastership.MastershipService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010065import org.onosproject.net.AnnotationKeys;
66import org.onosproject.net.ConnectPoint;
Amit Ghosh83c8c892017-11-09 11:08:27 +000067import org.onosproject.net.Device;
68import org.onosproject.net.DeviceId;
Amit Ghosh47243cb2017-07-26 05:08:53 +010069import org.onosproject.net.Host;
70import org.onosproject.net.Port;
Amit Ghosh83c8c892017-11-09 11:08:27 +000071import org.onosproject.net.PortNumber;
Amit Ghosh47243cb2017-07-26 05:08:53 +010072import org.onosproject.net.config.ConfigFactory;
73import org.onosproject.net.config.NetworkConfigEvent;
74import org.onosproject.net.config.NetworkConfigListener;
75import org.onosproject.net.config.NetworkConfigRegistry;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053076import org.onosproject.net.device.DeviceEvent;
77import org.onosproject.net.device.DeviceListener;
Amit Ghosh47243cb2017-07-26 05:08:53 +010078import org.onosproject.net.device.DeviceService;
79import org.onosproject.net.flow.DefaultTrafficSelector;
80import org.onosproject.net.flow.DefaultTrafficTreatment;
81import org.onosproject.net.flow.TrafficSelector;
82import org.onosproject.net.flow.TrafficTreatment;
Saurav Dasb4e3e102018-10-02 15:31:17 -070083import org.onosproject.net.flowobjective.FlowObjectiveService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010084import org.onosproject.net.host.HostService;
85import org.onosproject.net.packet.DefaultOutboundPacket;
86import org.onosproject.net.packet.OutboundPacket;
87import org.onosproject.net.packet.PacketContext;
88import org.onosproject.net.packet.PacketPriority;
89import org.onosproject.net.packet.PacketProcessor;
90import org.onosproject.net.packet.PacketService;
Matteo Scandolo57af5d12019-04-29 17:11:41 -070091import org.opencord.dhcpl2relay.DhcpAllocationInfo;
92import org.opencord.dhcpl2relay.DhcpL2RelayEvent;
93import org.opencord.dhcpl2relay.DhcpL2RelayListener;
94import org.opencord.dhcpl2relay.DhcpL2RelayService;
95import org.opencord.dhcpl2relay.impl.packet.DhcpOption82;
Gamze Abakac806c6c2018-12-03 12:49:46 +000096import org.opencord.sadis.BaseInformationService;
97import org.opencord.sadis.SadisService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010098import org.opencord.sadis.SubscriberAndDeviceInformation;
Amit Ghosh47243cb2017-07-26 05:08:53 +010099import org.osgi.service.component.ComponentContext;
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700100import org.osgi.service.component.annotations.Activate;
101import org.osgi.service.component.annotations.Component;
102import org.osgi.service.component.annotations.Deactivate;
103import org.osgi.service.component.annotations.Modified;
104import org.osgi.service.component.annotations.Reference;
105import org.osgi.service.component.annotations.ReferenceCardinality;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100106import org.slf4j.Logger;
107import org.slf4j.LoggerFactory;
108
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300109import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.*;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100110
111/**
112 * DHCP Relay Agent Application Component.
113 */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700114@Component(immediate = true,
115property = {
116 OPTION_82 + ":Boolean=" + OPTION_82_DEFAULT,
117 ENABLE_DHCP_BROADCAST_REPLIES + ":Boolean=" + ENABLE_DHCP_BROADCAST_REPLIES_DEFAULT,
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300118 PUBLISH_COUNTERS_RATE + ":Integer=" + PUBLISH_COUNTERS_RATE_DEFAULT,
119 DHCP_COUNTERS_TOPIC + ":String=" + DHCP_COUNTERS_TOPIC_DEFAULT
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700120})
Jonathan Hartc36c9552018-07-31 15:07:53 -0400121public class DhcpL2Relay
122 extends AbstractListenerManager<DhcpL2RelayEvent, DhcpL2RelayListener>
123 implements DhcpL2RelayService {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100124
125 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
Saurav Dasb4e3e102018-10-02 15:31:17 -0700126 private static final String HOST_LOC_PROVIDER =
127 "org.onosproject.provider.host.impl.HostLocationProvider";
Amit Ghosh47243cb2017-07-26 05:08:53 +0100128 private final Logger log = LoggerFactory.getLogger(getClass());
129 private final InternalConfigListener cfgListener =
130 new InternalConfigListener();
131
132 private final Set<ConfigFactory> factories = ImmutableSet.of(
133 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
134 DhcpL2RelayConfig.class,
135 "dhcpl2relay") {
136 @Override
137 public DhcpL2RelayConfig createConfig() {
138 return new DhcpL2RelayConfig();
139 }
140 }
141 );
142
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100144 protected NetworkConfigRegistry cfgService;
145
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100147 protected CoreService coreService;
148
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100150 protected PacketService packetService;
151
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100153 protected HostService hostService;
154
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100156 protected ComponentConfigService componentConfigService;
157
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Gamze Abakac806c6c2018-12-03 12:49:46 +0000159 protected SadisService sadisService;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100160
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh47243cb2017-07-26 05:08:53 +0100162 protected DeviceService deviceService;
163
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Amit Ghosh8951f042017-08-10 13:48:10 +0100165 protected MastershipService mastershipService;
166
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Saurav Dasb4e3e102018-10-02 15:31:17 -0700168 protected FlowObjectiveService flowObjectiveService;
169
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
171 protected DhcpL2RelayCountersStore dhcpL2RelayCounters;
172
173 // OSGi Properties
Carmelo Cascone4330cf12019-11-15 21:34:02 -0800174 /** Add option 82 to relayed packets. */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700175 protected boolean option82 = OPTION_82_DEFAULT;
Carmelo Cascone4330cf12019-11-15 21:34:02 -0800176 /** Ask the DHCP Server to send back replies as L2 broadcast. */
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700177 protected boolean enableDhcpBroadcastReplies = ENABLE_DHCP_BROADCAST_REPLIES_DEFAULT;
Amit Ghosha17354e2017-08-23 12:56:04 +0100178
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300179 protected int publishCountersRate = PUBLISH_COUNTERS_RATE_DEFAULT;
180 private String dhcpCountersTopic = DHCP_COUNTERS_TOPIC_DEFAULT;
181
182
183 protected PublishCountersToKafka publishCountersToKafka;
184
185 ScheduledFuture<?> refreshTask;
186 ScheduledExecutorService refreshService = Executors.newSingleThreadScheduledExecutor();
187
Amit Ghosh47243cb2017-07-26 05:08:53 +0100188 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
189 new DhcpRelayPacketProcessor();
190
Amit Ghosh8951f042017-08-10 13:48:10 +0100191 private InnerMastershipListener changeListener = new InnerMastershipListener();
192 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100193
Amit Ghosh8951f042017-08-10 13:48:10 +0100194 // connect points to the DHCP server
195 Set<ConnectPoint> dhcpConnectPoints;
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300196 protected AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100197 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
198 private ApplicationId appId;
199
Amit Ghosha17354e2017-08-23 12:56:04 +0100200 static Map<String, DhcpAllocationInfo> allocationMap = Maps.newConcurrentMap();
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300201 protected boolean modifyClientPktsSrcDstMac = false;
Amit Ghosh83c8c892017-11-09 11:08:27 +0000202 //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 -0300203 protected boolean useOltUplink = false;
Amit Ghosha17354e2017-08-23 12:56:04 +0100204
Gamze Abakac806c6c2018-12-03 12:49:46 +0000205 private BaseInformationService<SubscriberAndDeviceInformation> subsService;
206
Amit Ghosh47243cb2017-07-26 05:08:53 +0100207 @Activate
208 protected void activate(ComponentContext context) {
209 //start the dhcp relay agent
210 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700211 // ensure that host-learning via dhcp includes IP addresses
212 componentConfigService.preSetProperty(HOST_LOC_PROVIDER,
213 "useDhcp", Boolean.TRUE.toString());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100214 componentConfigService.registerProperties(getClass());
Jonathan Hartc36c9552018-07-31 15:07:53 -0400215 eventDispatcher.addSink(DhcpL2RelayEvent.class, listenerRegistry);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100216
217 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100218 mastershipService.addListener(changeListener);
219 deviceService.addListener(deviceListener);
220
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700221 subsService = sadisService.getSubscriberInfoService();
222
Amit Ghosh47243cb2017-07-26 05:08:53 +0100223 factories.forEach(cfgService::registerConfigFactory);
224 //update the dhcp server configuration.
225 updateConfig();
226 //add the packet services.
227 packetService.addProcessor(dhcpRelayPacketProcessor,
228 PacketProcessor.director(0));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000229 if (context != null) {
230 modified(context);
231 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100232
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300233 publishCountersToKafka = new PublishCountersToKafka();
234 subsService = sadisService.getSubscriberInfoService();
235 restartPublishCountersTask();
Gamze Abakac806c6c2018-12-03 12:49:46 +0000236
Amit Ghosh47243cb2017-07-26 05:08:53 +0100237 log.info("DHCP-L2-RELAY Started");
238 }
239
240 @Deactivate
241 protected void deactivate() {
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300242 if (refreshTask != null) {
243 refreshTask.cancel(true);
244 }
245 if (refreshService != null) {
246 refreshService.shutdownNow();
247 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100248 cfgService.removeListener(cfgListener);
249 factories.forEach(cfgService::unregisterConfigFactory);
250 packetService.removeProcessor(dhcpRelayPacketProcessor);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700251 cancelDhcpPktsFromServer();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100252
253 componentConfigService.unregisterProperties(getClass(), false);
Deepa Vaddireddy77a6ac72017-09-20 20:36:52 +0530254 deviceService.removeListener(deviceListener);
255 mastershipService.removeListener(changeListener);
Jonathan Hartc36c9552018-07-31 15:07:53 -0400256 eventDispatcher.removeSink(DhcpL2RelayEvent.class);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100257 log.info("DHCP-L2-RELAY Stopped");
258 }
259
260 @Modified
261 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000262
Amit Ghosh47243cb2017-07-26 05:08:53 +0100263 Dictionary<?, ?> properties = context.getProperties();
264
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700265 Boolean o = Tools.isPropertyEnabled(properties, OPTION_82);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100266 if (o != null) {
267 option82 = o;
268 }
Amit Ghosh2095dc62017-09-25 20:56:55 +0100269
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700270 o = Tools.isPropertyEnabled(properties, ENABLE_DHCP_BROADCAST_REPLIES);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100271 if (o != null) {
272 enableDhcpBroadcastReplies = o;
273 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300274
275 Integer newPublishCountersRate = getIntegerProperty(properties, "publishCountersRate");
276 if (newPublishCountersRate != null) {
277 if (newPublishCountersRate != publishCountersRate && newPublishCountersRate >= 0) {
278 log.info("publishCountersRate modified from {} to {}", publishCountersRate, newPublishCountersRate);
279 publishCountersRate = newPublishCountersRate;
280 } else if (newPublishCountersRate < 0) {
281 log.error("Invalid newPublishCountersRate : {}, defaulting to 0", newPublishCountersRate);
282 publishCountersRate = 0;
283 }
284 restartPublishCountersTask();
285 }
286
287 String newDhcpCountersTopic = get(properties, "dhcpCountersTopic");
288 if (newDhcpCountersTopic != null && !newDhcpCountersTopic.equals(dhcpCountersTopic)) {
289 log.info("Property dhcpCountersTopic modified from {} to {}", dhcpCountersTopic, newDhcpCountersTopic);
290 dhcpCountersTopic = newDhcpCountersTopic;
291 }
292 }
293
294 /**
295 * Starts a thread to publish the counters to kafka at a certain rate time.
296 */
297 private void restartPublishCountersTask() {
298 if (refreshTask != null) {
299 refreshTask.cancel(true);
300 }
301 if (publishCountersRate > 0) {
302 log.info("Refresh Rate set to {}, publishCountersToKafka will be called every {} seconds",
303 publishCountersRate, publishCountersRate);
304 refreshTask = refreshService.scheduleWithFixedDelay(SafeRecurringTask.wrap(publishCountersToKafka),
305 publishCountersRate, publishCountersRate, TimeUnit.SECONDS);
306 } else {
307 log.info("Refresh Rate set to 0, disabling calls to publishCountersToKafka");
308 }
309 }
310
311 /**
312 * Publish the counters to kafka.
313 */
314 private class PublishCountersToKafka implements Runnable {
315 public void run() {
316 dhcpL2RelayCounters.getCountersMap().forEach((counterKey, counterValue) -> {
317 // Publish the global counters
318 if (counterKey.counterClassKey.equals(DhcpL2RelayEvent.GLOBAL_COUNTER)) {
319 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.STATS_UPDATE, null, null,
320 new AbstractMap.SimpleEntry<String, AtomicLong>(counterKey.counterTypeKey.toString(),
321 counterValue), dhcpCountersTopic, null));
322 } else { // Publish the counters per subscriber
323 DhcpAllocationInfo info = allocationMap.get(counterKey.counterClassKey);
324 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.STATS_UPDATE, info, null,
325 new AbstractMap.SimpleEntry<String, AtomicLong>(counterKey.counterTypeKey.toString(),
326 counterValue), dhcpCountersTopic, counterKey.counterClassKey));
327 }
328 });
329 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100330 }
331
332 /**
333 * Checks if this app has been configured.
334 *
335 * @return true if all information we need have been initialized
336 */
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300337 protected boolean configured() {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000338 if (!useOltUplink) {
339 return dhcpServerConnectPoint.get() != null;
340 }
341 return true;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100342 }
343
Amit Ghosh8951f042017-08-10 13:48:10 +0100344 /**
345 * Selects a connect point through an available device for which it is the master.
346 */
347 private void selectServerConnectPoint() {
348 synchronized (this) {
349 dhcpServerConnectPoint.set(null);
350 if (dhcpConnectPoints != null) {
351 // find a connect point through a device for which we are master
352 for (ConnectPoint cp: dhcpConnectPoints) {
353 if (mastershipService.isLocalMaster(cp.deviceId())) {
354 if (deviceService.isAvailable(cp.deviceId())) {
355 dhcpServerConnectPoint.set(cp);
356 }
357 log.info("DHCP connectPoint selected is {}", cp);
358 break;
359 }
360 }
361 }
362
363 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
364
365 if (dhcpServerConnectPoint.get() == null) {
366 log.error("Master of none, can't relay DHCP Message to server");
367 }
368 }
369 }
370
371 /**
372 * Updates the network configuration.
373 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100374 private void updateConfig() {
375 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
376 if (cfg == null) {
377 log.warn("Dhcp Server info not available");
378 return;
379 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100380
381 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
Amit Ghosh83c8c892017-11-09 11:08:27 +0000382 modifyClientPktsSrcDstMac = cfg.getModifySrcDstMacAddresses();
Saurav Dasb4e3e102018-10-02 15:31:17 -0700383 boolean prevUseOltUplink = useOltUplink;
Amit Ghosh83c8c892017-11-09 11:08:27 +0000384 useOltUplink = cfg.getUseOltUplinkForServerPktInOut();
Amit Ghosh8951f042017-08-10 13:48:10 +0100385
Saurav Dasb4e3e102018-10-02 15:31:17 -0700386 if (useOltUplink) {
387 for (ConnectPoint cp : getUplinkPortsOfOlts()) {
388 log.debug("requestDhcpPackets: ConnectPoint: {}", cp);
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700389 requestDhcpPacketsFromConnectPoint(cp, Optional.ofNullable(null));
Saurav Dasb4e3e102018-10-02 15:31:17 -0700390 }
391 // check if previous config was different and so trap flows may
Saurav Dasb14f08a2019-02-22 16:34:15 -0800392 // need to be removed from other places like AGG switches
Saurav Dasb4e3e102018-10-02 15:31:17 -0700393 if (!prevUseOltUplink) {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800394 addOrRemoveDhcpTrapFromServer(false);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700395 }
Saurav Dasb4e3e102018-10-02 15:31:17 -0700396 } else {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800397 // uplink on AGG switch
398 addOrRemoveDhcpTrapFromServer(true);
Saurav Dasb4e3e102018-10-02 15:31:17 -0700399 }
400 }
401
402 private void cancelDhcpPktsFromServer() {
403 if (useOltUplink) {
404 for (ConnectPoint cp : getUplinkPortsOfOlts()) {
405 log.debug("cancelDhcpPackets: ConnectPoint: {}", cp);
Matteo Scandolo45e5a272019-09-30 09:30:32 -0700406 cancelDhcpPacketsFromConnectPoint(cp, Optional.ofNullable(null));
Saurav Dasb4e3e102018-10-02 15:31:17 -0700407 }
408 } else {
Saurav Dasb14f08a2019-02-22 16:34:15 -0800409 // uplink on AGG switch
410 addOrRemoveDhcpTrapFromServer(false);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000411 }
Saurav Dasb4e3e102018-10-02 15:31:17 -0700412 }
413
Saurav Dasb14f08a2019-02-22 16:34:15 -0800414 /**
415 * Used to add or remove DHCP trap flow for packets received from DHCP server.
416 * Typically used on a non OLT device, like an AGG switch. When adding, a
417 * new dhcp server connect point is selected from the configured options.
418 *
419 * @param add true if dhcp trap flow is to be added, false to remove the
420 * trap flow
421 */
422 private void addOrRemoveDhcpTrapFromServer(boolean add) {
423 if (add) {
424 selectServerConnectPoint();
425 log.debug("dhcp server connect point: " + dhcpServerConnectPoint);
426 }
427 if (dhcpServerConnectPoint.get() == null) {
428 log.warn("No dhcpServer connectPoint found, cannot {} dhcp trap flows",
429 (add) ? "install" : "remove");
430 return;
431 }
432 if (add) {
433 log.info("Adding trap to dhcp server connect point: "
434 + dhcpServerConnectPoint);
435 requestDhcpPacketsFromConnectPoint(dhcpServerConnectPoint.get(),
436 Optional.of(PacketPriority.HIGH1));
437 } else {
438 log.info("Removing trap from dhcp server connect point: "
439 + dhcpServerConnectPoint);
440 cancelDhcpPacketsFromConnectPoint(dhcpServerConnectPoint.get(),
441 Optional.of(PacketPriority.HIGH1));
442 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100443 }
444
445 /**
Amit Ghosh83c8c892017-11-09 11:08:27 +0000446 * Returns all the uplink ports of OLTs configured in SADIS.
447 * Only ports visible in ONOS and for which this instance is master
448 * are returned
449 */
450 private List<ConnectPoint> getUplinkPortsOfOlts() {
451 List<ConnectPoint> cps = new ArrayList<>();
452
453 // find all the olt devices and if their uplink ports are visible
454 Iterable<Device> devices = deviceService.getDevices();
455 for (Device d : devices) {
456 // check if this device is provisioned in Sadis
457
458 log.debug("getUplinkPortsOfOlts: Checking mastership of {}", d);
459 // do only for devices for which we are the master
460 if (!mastershipService.isLocalMaster(d.id())) {
461 continue;
462 }
463
464 String devSerialNo = d.serialNumber();
465 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
466 log.debug("getUplinkPortsOfOlts: Found device: {}", deviceInfo);
467 if (deviceInfo != null) {
468 // check if the uplink port with that number is available on the device
469 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
470 Port port = deviceService.getPort(d.id(), pNum);
471 log.debug("getUplinkPortsOfOlts: Found port: {}", port);
472 if (port != null) {
473 cps.add(new ConnectPoint(d.id(), pNum));
474 }
475 }
476 }
477 return cps;
478 }
479
480 /**
481 * Returns whether the passed port is the uplink port of the olt device.
482 */
483 private boolean isUplinkPortOfOlt(DeviceId dId, Port p) {
484 log.debug("isUplinkPortOfOlt: DeviceId: {} Port: {}", dId, p);
485 // do only for devices for which we are the master
486 if (!mastershipService.isLocalMaster(dId)) {
487 return false;
488 }
489
490 Device d = deviceService.getDevice(dId);
491 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
492
493 if (deviceInfo != null) {
494 return (deviceInfo.uplinkPort() == p.number().toLong());
495 }
496
497 return false;
498 }
499
500 /**
501 * Returns the connectPoint which is the uplink port of the OLT.
502 */
503 private ConnectPoint getUplinkConnectPointOfOlt(DeviceId dId) {
504
505 Device d = deviceService.getDevice(dId);
506 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
507 log.debug("getUplinkConnectPointOfOlt DeviceId: {} devInfo: {}", dId, deviceInfo);
508 if (deviceInfo != null) {
509 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
510 Port port = deviceService.getPort(d.id(), pNum);
511 if (port != null) {
512 return new ConnectPoint(d.id(), pNum);
513 }
514 }
515
516 return null;
517 }
518
519 /**
520 * Request DHCP packet from particular connect point via PacketService.
Saurav Dasb14f08a2019-02-22 16:34:15 -0800521 * Optionally provide a priority for the trap flow. If no such priority is
522 * provided, the default priority will be used.
523 *
524 * @param cp the connect point to trap dhcp packets from
525 * @param priority of the trap flow, null to use default priority
Amit Ghosh83c8c892017-11-09 11:08:27 +0000526 */
Saurav Dasb14f08a2019-02-22 16:34:15 -0800527 private void requestDhcpPacketsFromConnectPoint(ConnectPoint cp,
528 Optional<PacketPriority> priority) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000529 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
530 .matchEthType(Ethernet.TYPE_IPV4)
531 .matchInPort(cp.port())
532 .matchIPProtocol(IPv4.PROTOCOL_UDP)
533 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
534 packetService.requestPackets(selectorServer.build(),
Saurav Dasb14f08a2019-02-22 16:34:15 -0800535 priority.isPresent() ? priority.get() : PacketPriority.CONTROL,
536 appId, Optional.of(cp.deviceId()));
Amit Ghosh83c8c892017-11-09 11:08:27 +0000537 }
538
539 /**
Saurav Dasb14f08a2019-02-22 16:34:15 -0800540 * Cancel DHCP packet from particular connect point via PacketService. If
541 * the request was made with a specific packet priority, then the same
542 * priority should be used in this call.
543 *
544 * @param cp the connect point for the trap flow
545 * @param priority with which the trap flow was requested; if request
546 * priority was not specified, this param should also be null
Amit Ghosh83c8c892017-11-09 11:08:27 +0000547 */
Saurav Dasb14f08a2019-02-22 16:34:15 -0800548 private void cancelDhcpPacketsFromConnectPoint(ConnectPoint cp,
549 Optional<PacketPriority> priority) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000550 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
551 .matchEthType(Ethernet.TYPE_IPV4)
552 .matchInPort(cp.port())
553 .matchIPProtocol(IPv4.PROTOCOL_UDP)
554 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
555 packetService.cancelPackets(selectorServer.build(),
Saurav Dasb14f08a2019-02-22 16:34:15 -0800556 priority.isPresent() ? priority.get() : PacketPriority.CONTROL,
557 appId, Optional.of(cp.deviceId()));
Amit Ghosh83c8c892017-11-09 11:08:27 +0000558 }
559
Amit Ghosha17354e2017-08-23 12:56:04 +0100560 public static Map<String, DhcpAllocationInfo> allocationMap() {
561 return allocationMap;
562 }
563
Amit Ghosh47243cb2017-07-26 05:08:53 +0100564 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
565 String serialNo = deviceService.getDevice(context.inPacket().
566 receivedFrom().deviceId()).serialNumber();
567
568 return subsService.get(serialNo);
569 }
570
571 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
572 String serialNo = deviceService.getDevice(cp.deviceId()).
573 serialNumber();
574
575 return subsService.get(serialNo);
576 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100577
578 private MacAddress relayAgentMacAddress(PacketContext context) {
579
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000580 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100581 if (device == null) {
582 log.warn("Device not found for {}", context.inPacket().
583 receivedFrom());
584 return null;
585 }
586
587 return device.hardwareIdentifier();
588 }
589
590 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100591 return nasPortId(context.inPacket().receivedFrom());
592 }
593
594 private String nasPortId(ConnectPoint cp) {
595 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100596 return p.annotations().value(AnnotationKeys.PORT_NAME);
597 }
598
599 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100600 return subsService.get(nasPortId(context));
601 }
602
603 private VlanId cTag(PacketContext context) {
604 SubscriberAndDeviceInformation sub = getSubscriber(context);
605 if (sub == null) {
606 log.warn("Subscriber info not found for {}", context.inPacket().
607 receivedFrom());
608 return VlanId.NONE;
609 }
610 return sub.cTag();
611 }
612
Amit Ghosh8951f042017-08-10 13:48:10 +0100613 private VlanId cTag(ConnectPoint cp) {
614 String portId = nasPortId(cp);
615 SubscriberAndDeviceInformation sub = subsService.get(portId);
616 if (sub == null) {
617 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
618 return VlanId.NONE;
619 }
620 return sub.cTag();
621 }
622
623 private VlanId sTag(ConnectPoint cp) {
624 String portId = nasPortId(cp);
625 SubscriberAndDeviceInformation sub = subsService.get(portId);
626 if (sub == null) {
627 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
628 return VlanId.NONE;
629 }
630 return sub.sTag();
631 }
632
Amit Ghosh47243cb2017-07-26 05:08:53 +0100633 private VlanId sTag(PacketContext context) {
634 SubscriberAndDeviceInformation sub = getSubscriber(context);
635 if (sub == null) {
636 log.warn("Subscriber info not found for {}", context.inPacket().
637 receivedFrom());
638 return VlanId.NONE;
639 }
640 return sub.sTag();
641 }
642
643 private class DhcpRelayPacketProcessor implements PacketProcessor {
644
645 @Override
646 public void process(PacketContext context) {
647 if (!configured()) {
648 log.warn("Missing DHCP relay config. Abort packet processing");
649 return;
650 }
651
652 // process the packet and get the payload
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530653 Ethernet packet = context.inPacket().parsed();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000654
Amit Ghosh47243cb2017-07-26 05:08:53 +0100655 if (packet == null) {
656 log.warn("Packet is null");
657 return;
658 }
659
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530660 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100661 IPv4 ipv4Packet = (IPv4) packet.getPayload();
662
663 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
664 UDP udpPacket = (UDP) ipv4Packet.getPayload();
665 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000666 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100667 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
668 //This packet is dhcp.
669 processDhcpPacket(context, packet, dhcpPayload);
670 }
671 }
672 }
673 }
674
675 //forward the packet to ConnectPoint where the DHCP server is attached.
Amit Ghosh83c8c892017-11-09 11:08:27 +0000676 private void forwardPacket(Ethernet packet, PacketContext context) {
677 ConnectPoint toSendTo = null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100678
Amit Ghosh83c8c892017-11-09 11:08:27 +0000679 if (!useOltUplink) {
680 toSendTo = dhcpServerConnectPoint.get();
681 } else {
682 toSendTo = getUplinkConnectPointOfOlt(context.inPacket().
683 receivedFrom().deviceId());
684 }
685
686 if (toSendTo != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100687 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh83c8c892017-11-09 11:08:27 +0000688 .setOutput(toSendTo.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100689 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh83c8c892017-11-09 11:08:27 +0000690 toSendTo.deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100691 ByteBuffer.wrap(packet.serialize()));
692 if (log.isTraceEnabled()) {
Saurav Das15626a02018-09-27 18:36:45 -0700693 log.trace("Relaying packet to dhcp server at {} {}",
694 toSendTo, packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100695 }
696 packetService.emit(o);
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300697
698 SubscriberAndDeviceInformation entry = getSubscriberInfoFromClient(context);
699 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("PACKETS_TO_SERVER"));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100700 } else {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000701 log.error("No connect point to send msg to DHCP Server");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100702 }
703 }
704
Amit Ghosha17354e2017-08-23 12:56:04 +0100705 // get the type of the DHCP packet
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700706 private DHCP.MsgType getDhcpPacketType(DHCP dhcpPayload) {
Amit Ghosha17354e2017-08-23 12:56:04 +0100707
Jonathan Hartedbf6422018-05-02 17:30:05 -0700708 for (DhcpOption option : dhcpPayload.getOptions()) {
Amit Ghosha17354e2017-08-23 12:56:04 +0100709 if (option.getCode() == OptionCode_MessageType.getValue()) {
710 byte[] data = option.getData();
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700711 return DHCP.MsgType.getType(data[0]);
Amit Ghosha17354e2017-08-23 12:56:04 +0100712 }
713 }
714 return null;
715 }
716
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300717 private void updateDhcpRelayCountersStore(SubscriberAndDeviceInformation entry,
718 DhcpL2RelayCounters counterType) {
719 // Update global counter stats
720 dhcpL2RelayCounters.incrementCounter(DhcpL2RelayEvent.GLOBAL_COUNTER, counterType);
721 if (entry == null) {
722 log.warn("Counter not updated as subscriber info not found.");
723 } else {
724 // Update subscriber counter stats
725 dhcpL2RelayCounters.incrementCounter(entry.id(), counterType);
726 }
727 }
728
729 /*
730 * Get subscriber information based on it's context packet.
731 */
732 private SubscriberAndDeviceInformation getSubscriberInfoFromClient(PacketContext context) {
733 if (context != null) {
734 return getSubscriber(context);
735 }
736 return null;
737 }
738
739 /*
740 * Get subscriber information based on it's DHCP payload.
741 */
742 private SubscriberAndDeviceInformation getSubscriberInfoFromServer(DHCP dhcpPayload) {
743 if (dhcpPayload != null) {
744 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
745 ConnectPoint subsCp = getConnectPointOfClient(descMac);
746
747 if (subsCp != null) {
748 String portId = nasPortId(subsCp);
749 return subsService.get(portId);
750 }
751 }
752 return null;
753 }
754
Amit Ghosh47243cb2017-07-26 05:08:53 +0100755 //process the dhcp packet before sending to server
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530756 private void processDhcpPacket(PacketContext context, Ethernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100757 DHCP dhcpPayload) {
758 if (dhcpPayload == null) {
759 log.warn("DHCP payload is null");
760 return;
761 }
762
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700763 DHCP.MsgType incomingPacketType = getDhcpPacketType(dhcpPayload);
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300764 if (incomingPacketType == null) {
765 log.warn("DHCP packet type not found. Dump of ethernet pkt in hex format for troubleshooting.");
766 byte[] array = packet.serialize();
767 ByteArrayOutputStream buf = new ByteArrayOutputStream();
768 try {
769 HexDump.dump(array, 0, buf, 0);
770 log.trace(buf.toString());
771 } catch (Exception e) { }
772 return;
773 }
774
775 SubscriberAndDeviceInformation entry = null;
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000776
Saurav Das15626a02018-09-27 18:36:45 -0700777 log.info("Received DHCP Packet of type {} from {}",
778 incomingPacketType, context.inPacket().receivedFrom());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100779
780 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000781 case DHCPDISCOVER:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530782 Ethernet ethernetPacketDiscover =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000783 processDhcpPacketFromClient(context, packet);
784 if (ethernetPacketDiscover != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000785 forwardPacket(ethernetPacketDiscover, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000786 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300787 entry = getSubscriberInfoFromClient(context);
788 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("DHCPDISCOVER"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000789 break;
790 case DHCPOFFER:
791 //reply to dhcp client.
Saurav Das15626a02018-09-27 18:36:45 -0700792 Ethernet ethernetPacketOffer =
793 processDhcpPacketFromServer(context, packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000794 if (ethernetPacketOffer != null) {
795 sendReply(ethernetPacketOffer, dhcpPayload);
796 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300797 entry = getSubscriberInfoFromServer(dhcpPayload);
798 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("DHCPOFFER"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000799 break;
800 case DHCPREQUEST:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530801 Ethernet ethernetPacketRequest =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000802 processDhcpPacketFromClient(context, packet);
803 if (ethernetPacketRequest != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000804 forwardPacket(ethernetPacketRequest, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000805 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300806 entry = getSubscriberInfoFromClient(context);
807 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("DHCPREQUEST"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000808 break;
809 case DHCPACK:
810 //reply to dhcp client.
Saurav Das15626a02018-09-27 18:36:45 -0700811 Ethernet ethernetPacketAck =
812 processDhcpPacketFromServer(context, packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000813 if (ethernetPacketAck != null) {
814 sendReply(ethernetPacketAck, dhcpPayload);
815 }
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300816 entry = getSubscriberInfoFromServer(dhcpPayload);
817 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("DHCPACK"));
818 break;
819 case DHCPDECLINE:
820 entry = getSubscriberInfoFromClient(context);
821 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("DHCPDECLINE"));
822 break;
823 case DHCPNAK:
824 entry = getSubscriberInfoFromServer(dhcpPayload);
825 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("DHCPNACK"));
826 break;
827 case DHCPRELEASE:
828 entry = getSubscriberInfoFromClient(context);
829 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("DHCPRELEASE"));
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000830 break;
831 default:
832 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100833 }
834 }
835
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530836 private Ethernet processDhcpPacketFromClient(PacketContext context,
837 Ethernet ethernetPacket) {
Saurav Das15626a02018-09-27 18:36:45 -0700838 if (log.isTraceEnabled()) {
839 log.trace("DHCP packet received from client at {} {}",
840 context.inPacket().receivedFrom(), ethernetPacket);
841 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100842
843 MacAddress relayAgentMac = relayAgentMacAddress(context);
844 if (relayAgentMac == null) {
845 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100846 return null;
847 }
848
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530849 Ethernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100850
851 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
852 UDP udpPacket = (UDP) ipv4Packet.getPayload();
853 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
854
Amit Ghosha17354e2017-08-23 12:56:04 +0100855 if (enableDhcpBroadcastReplies) {
856 // We want the reply to come back as a L2 broadcast
857 dhcpPacket.setFlags((short) 0x8000);
858 }
859
Jonathan Hartc36c9552018-07-31 15:07:53 -0400860 MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
861 IpAddress clientIp = IpAddress.valueOf(dhcpPacket.getClientIPAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100862
Jonathan Hartc36c9552018-07-31 15:07:53 -0400863 SubscriberAndDeviceInformation entry = getSubscriber(context);
864 if (entry == null) {
Saurav Das15626a02018-09-27 18:36:45 -0700865 log.warn("Dropping packet as subscriber entry is not available");
Jonathan Hartc36c9552018-07-31 15:07:53 -0400866 return null;
867 }
868
869 DhcpAllocationInfo info = new DhcpAllocationInfo(
870 context.inPacket().receivedFrom(), dhcpPacket.getPacketType(),
871 entry.nasPortId(), clientMac, clientIp);
872
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300873 allocationMap.put(entry.id(), info);
Jonathan Hartc36c9552018-07-31 15:07:53 -0400874
Saurav Das15626a02018-09-27 18:36:45 -0700875 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.UPDATED, info,
876 context.inPacket().receivedFrom()));
Amit Ghosha17354e2017-08-23 12:56:04 +0100877
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000878 if (option82) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000879 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
880 udpPacket.setPayload(dhcpPacketWithOption82);
881 }
882
883 ipv4Packet.setPayload(udpPacket);
884 etherReply.setPayload(ipv4Packet);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000885 if (modifyClientPktsSrcDstMac) {
886 etherReply.setSourceMACAddress(relayAgentMac);
887 etherReply.setDestinationMACAddress(dhcpConnectMac);
888 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100889
Amit Ghosh8951f042017-08-10 13:48:10 +0100890 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100891 etherReply.setVlanID(cTag(context).toShort());
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530892 etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
893 etherReply.setQinQVID(sTag(context).toShort());
Saurav Das15626a02018-09-27 18:36:45 -0700894 log.info("Finished processing packet.. relaying to dhcpServer");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100895 return etherReply;
896 }
897
898 //build the DHCP offer/ack with proper client port.
Saurav Das15626a02018-09-27 18:36:45 -0700899 private Ethernet processDhcpPacketFromServer(PacketContext context,
900 Ethernet ethernetPacket) {
901 if (log.isTraceEnabled()) {
902 log.trace("DHCP packet received from server at {} {}",
903 context.inPacket().receivedFrom(), ethernetPacket);
904 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100905 // get dhcp header.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530906 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100907 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
908 UDP udpPacket = (UDP) ipv4Packet.getPayload();
909 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
910
Amit Ghosh47243cb2017-07-26 05:08:53 +0100911 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100912 ConnectPoint subsCp = getConnectPointOfClient(dstMac);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100913 // If we can't find the subscriber, can't process further
914 if (subsCp == null) {
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700915 log.warn("Couldn't find connection point for mac address {} DHCPOFFERs won't be delivered", dstMac);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100916 return null;
917 }
Amit Ghosha17354e2017-08-23 12:56:04 +0100918 // if it's an ACK packet store the information for display purpose
Carmelo Casconede1e6e32019-07-15 19:39:08 -0700919 if (getDhcpPacketType(dhcpPayload) == DHCP.MsgType.DHCPACK) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100920
Amit Ghosha17354e2017-08-23 12:56:04 +0100921 String portId = nasPortId(subsCp);
922 SubscriberAndDeviceInformation sub = subsService.get(portId);
923 if (sub != null) {
Jonathan Hartedbf6422018-05-02 17:30:05 -0700924 List<DhcpOption> options = dhcpPayload.getOptions();
925 List<DhcpOption> circuitIds = options.stream()
Amit Ghosha17354e2017-08-23 12:56:04 +0100926 .filter(option -> option.getCode() == DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
927 .collect(Collectors.toList());
928
929 String circuitId = "None";
930 if (circuitIds.size() == 1) {
Amit Ghosh2095dc62017-09-25 20:56:55 +0100931 byte[] array = circuitIds.get(0).getData();
932
933 try {
934 // we leave the first two bytes as they are the id and length
935 circuitId = new String(Arrays.copyOfRange(array, 2, array.length), "UTF-8");
936 } catch (Exception e) { }
Amit Ghosha17354e2017-08-23 12:56:04 +0100937 }
938
939 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
940
941 //storeDHCPAllocationInfo
Jonathan Hartc36c9552018-07-31 15:07:53 -0400942 DhcpAllocationInfo info = new DhcpAllocationInfo(subsCp,
943 dhcpPayload.getPacketType(), circuitId, dstMac, ip);
Amit Ghosha17354e2017-08-23 12:56:04 +0100944
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300945 allocationMap.put(sub.id(), info);
Jonathan Hartc36c9552018-07-31 15:07:53 -0400946
947 post(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.UPDATED, info, subsCp));
Amit Ghosha17354e2017-08-23 12:56:04 +0100948 }
949 } // end storing of info
Amit Ghosh47243cb2017-07-26 05:08:53 +0100950
Marcos Aurelio Carreroeaf02b82019-11-25 13:34:25 -0300951 SubscriberAndDeviceInformation entry = getSubscriberInfoFromServer(dhcpPayload);
952 updateDhcpRelayCountersStore(entry, DhcpL2RelayCounters.valueOf("PACKETS_FROM_SERVER"));
953
Amit Ghosh47243cb2017-07-26 05:08:53 +0100954 // we leave the srcMac from the original packet
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530955 etherReply.setDestinationMACAddress(dstMac);
956 etherReply.setQinQVID(sTag(subsCp).toShort());
Amit Ghosh8951f042017-08-10 13:48:10 +0100957 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
958 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100959
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000960 if (option82) {
961 udpPacket.setPayload(removeOption82(dhcpPayload));
962 } else {
963 udpPacket.setPayload(dhcpPayload);
964 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100965 ipv4Packet.setPayload(udpPacket);
966 etherReply.setPayload(ipv4Packet);
967
Saurav Das15626a02018-09-27 18:36:45 -0700968 log.info("Finished processing packet.. relaying to client");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100969 return etherReply;
970 }
971
Amit Ghosha17354e2017-08-23 12:56:04 +0100972 /*
973 * Get ConnectPoint of the Client based on it's MAC address
974 */
975 private ConnectPoint getConnectPointOfClient(MacAddress dstMac) {
976 Set<Host> hosts = hostService.getHostsByMac(dstMac);
977 if (hosts == null || hosts.isEmpty()) {
978 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530979 + "relay for dhcp packet from server",
Amit Ghosha17354e2017-08-23 12:56:04 +0100980 dstMac);
981 return null;
982 }
983 for (Host h : hosts) {
984 // if more than one,
985 // find the connect point which has an valid entry in SADIS
986 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
987 h.location().port());
988
989 if (sTag(cp) != VlanId.NONE) {
990 return cp;
991 }
992 }
993
994 return null;
995 }
996
Amit Ghosh47243cb2017-07-26 05:08:53 +0100997 //send the response to the requester host.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530998 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100999 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +01001000 ConnectPoint subCp = getConnectPointOfClient(descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001001
1002 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +01001003 if (subCp != null) {
Saurav Das15626a02018-09-27 18:36:45 -07001004 log.info("Sending DHCP packet to client at {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001005 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +01001006 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +01001007 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +01001008 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +01001009 if (log.isTraceEnabled()) {
Saurav Das15626a02018-09-27 18:36:45 -07001010 log.trace("Relaying packet to dhcp client at {} {}", subCp,
1011 ethPacket);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001012 }
1013 packetService.emit(o);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001014 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001015 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +01001016 }
1017 }
1018 }
1019
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001020 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
1021 log.debug("option82data {} ", entry);
1022
Jonathan Hartedbf6422018-05-02 17:30:05 -07001023 List<DhcpOption> options = Lists.newArrayList(dhcpPacket.getOptions());
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001024 DhcpOption82 option82 = new DhcpOption82();
1025 option82.setAgentCircuitId(entry.circuitId());
1026 option82.setAgentRemoteId(entry.remoteId());
Jonathan Hartedbf6422018-05-02 17:30:05 -07001027 DhcpOption option = new DhcpOption()
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +05301028 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
1029 .setData(option82.toByteArray())
1030 .setLength(option82.length());
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001031
1032 options.add(options.size() - 1, option);
1033 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +01001034
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001035 return dhcpPacket;
1036
1037 }
1038
1039 private DHCP removeOption82(DHCP dhcpPacket) {
Jonathan Hartedbf6422018-05-02 17:30:05 -07001040 List<DhcpOption> options = dhcpPacket.getOptions();
1041 List<DhcpOption> newoptions = options.stream()
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001042 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
1043 .collect(Collectors.toList());
1044
1045 return dhcpPacket.setOptions(newoptions);
1046 }
Amit Ghosh47243cb2017-07-26 05:08:53 +01001047 /**
1048 * Listener for network config events.
1049 */
1050 private class InternalConfigListener implements NetworkConfigListener {
1051
1052 @Override
1053 public void event(NetworkConfigEvent event) {
1054
1055 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
1056 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
1057 event.configClass().equals(DhcpL2RelayConfig.class)) {
1058 updateConfig();
1059 log.info("Reconfigured");
1060 }
1061 }
1062 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001063
Amit Ghosh8951f042017-08-10 13:48:10 +01001064 /**
1065 * Handles Mastership changes for the devices which connect
1066 * to the DHCP server.
1067 */
1068 private class InnerMastershipListener implements MastershipListener {
1069 @Override
1070 public void event(MastershipEvent event) {
Amit Ghosh83c8c892017-11-09 11:08:27 +00001071 if (!useOltUplink) {
1072 if (dhcpServerConnectPoint.get() != null &&
1073 dhcpServerConnectPoint.get().deviceId().
1074 equals(event.subject())) {
1075 log.trace("Mastership Event recevived for {}", event.subject());
1076 // mastership of the device for our connect point has changed
1077 // reselect
1078 selectServerConnectPoint();
1079 }
Amit Ghosh8951f042017-08-10 13:48:10 +01001080 }
1081 }
1082 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +00001083
Amit Ghosh8951f042017-08-10 13:48:10 +01001084 /**
1085 * Handles Device status change for the devices which connect
1086 * to the DHCP server.
1087 */
1088 private class InnerDeviceListener implements DeviceListener {
1089 @Override
1090 public void event(DeviceEvent event) {
Thomas Lee S9df15082019-12-23 11:31:15 +05301091 switch (event.type()) {
1092 case DEVICE_AVAILABILITY_CHANGED:
1093 log.info("Device Avail Changed {}", event.subject().id());
1094 DeviceId deviceId = event.subject().id();
1095 if (!deviceService.isAvailable(deviceId)) {
1096 log.warn("Device {} is not available ", deviceId);
1097 allocationMap.entrySet().removeIf(entry -> deviceId.equals(entry.getValue().
1098 location().deviceId()));
1099 log.info("Device {} is removed from DHCP allocationmap ", deviceId);
1100 }
1101 break;
1102 default:
1103 break;
1104 }
Saurav Das15626a02018-09-27 18:36:45 -07001105 if (log.isTraceEnabled() &&
1106 !event.type().equals(DeviceEvent.Type.PORT_STATS_UPDATED)) {
1107 log.trace("Device Event received for {} event {}",
1108 event.subject(), event.type());
1109 }
Amit Ghosh83c8c892017-11-09 11:08:27 +00001110 if (!useOltUplink) {
1111 if (dhcpServerConnectPoint.get() == null) {
1112 switch (event.type()) {
1113 case DEVICE_ADDED:
1114 case DEVICE_AVAILABILITY_CHANGED:
Saurav Dasb14f08a2019-02-22 16:34:15 -08001115 // some device is available check if we can get a
1116 // connect point we can use
1117 addOrRemoveDhcpTrapFromServer(true);
Amit Ghosh83c8c892017-11-09 11:08:27 +00001118 break;
1119 default:
1120 break;
1121 }
1122 return;
Amit Ghosh8951f042017-08-10 13:48:10 +01001123 }
Amit Ghosh83c8c892017-11-09 11:08:27 +00001124 if (dhcpServerConnectPoint.get().deviceId().
1125 equals(event.subject().id())) {
1126 switch (event.type()) {
1127 case DEVICE_AVAILABILITY_CHANGED:
1128 case DEVICE_REMOVED:
1129 case DEVICE_SUSPENDED:
1130 // state of our device has changed, check if we need
Saurav Dasb14f08a2019-02-22 16:34:15 -08001131 // to re-select a connectpoint
1132 addOrRemoveDhcpTrapFromServer(true);
Amit Ghosh83c8c892017-11-09 11:08:27 +00001133 break;
1134 default:
1135 break;
1136 }
1137 }
1138 } else {
Amit Ghosh8951f042017-08-10 13:48:10 +01001139 switch (event.type()) {
Amit Ghosh83c8c892017-11-09 11:08:27 +00001140 case PORT_ADDED:
Saurav Dasb4e3e102018-10-02 15:31:17 -07001141 if (useOltUplink && isUplinkPortOfOlt(event.subject().id(), event.port())) {
Saurav Dasb14f08a2019-02-22 16:34:15 -08001142 requestDhcpPacketsFromConnectPoint(
1143 new ConnectPoint(event.subject().id(), event.port().number()),
1144 null);
Amit Ghosh83c8c892017-11-09 11:08:27 +00001145 }
Amit Ghosh8951f042017-08-10 13:48:10 +01001146 break;
1147 default:
1148 break;
1149 }
1150 }
1151 }
1152 }
Amit Ghosh47243cb2017-07-26 05:08:53 +01001153}