blob: 7f935dcb0c9bb3e7e20961e558c27ec93e0a67a5 [file] [log] [blame]
Yi Tseng7a38f9a2017-06-09 14:36:40 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Yi Tseng7a38f9a2017-06-09 14:36:40 -07003 *
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 */
16package org.onosproject.dhcprelay;
17
18import java.nio.ByteBuffer;
19import java.util.Collection;
Yi Tseng51f1be92017-09-01 17:24:57 -070020import java.util.Collections;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070021import java.util.Dictionary;
Yi Tseng51f1be92017-09-01 17:24:57 -070022import java.util.List;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070023import java.util.Objects;
24import java.util.Optional;
25import java.util.Set;
Yi Tseng525ff402017-10-23 19:39:39 -070026import java.util.stream.Collectors;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070027import java.util.stream.Stream;
28
Yi Tseng51f1be92017-09-01 17:24:57 -070029import com.google.common.collect.ImmutableList;
Ruchi Sahota196a9ca2019-03-01 16:56:07 +000030import com.google.common.collect.ImmutableSet;
Yi Tseng525ff402017-10-23 19:39:39 -070031import com.google.common.collect.Streams;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070032import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Modified;
36import org.apache.felix.scr.annotations.Property;
37import org.apache.felix.scr.annotations.Reference;
38import org.apache.felix.scr.annotations.ReferenceCardinality;
39import org.apache.felix.scr.annotations.Service;
40import org.onlab.packet.ARP;
41import org.onlab.packet.DHCP;
42import org.onlab.packet.DHCP6;
43import org.onlab.packet.IPacket;
44import org.onlab.packet.IPv6;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070045import org.onlab.packet.Ethernet;
46import org.onlab.packet.IPv4;
Yi Tseng4f2a0462017-08-31 11:21:00 -070047import org.onlab.packet.Ip4Address;
Anjali K Kf3963f12019-04-16 19:27:30 +000048import org.onlab.packet.Ip6Address;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070049import org.onlab.packet.MacAddress;
Kalhee Kimba366062017-11-07 16:32:09 +000050import org.onlab.packet.IpPrefix;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070051import org.onlab.packet.UDP;
52import org.onlab.packet.VlanId;
Anjali K Kf3963f12019-04-16 19:27:30 +000053import org.onlab.packet.ndp.NeighborSolicitation;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070054import org.onlab.util.Tools;
55import org.onosproject.cfg.ComponentConfigService;
56import org.onosproject.core.ApplicationId;
57import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070058import org.onosproject.dhcprelay.api.DhcpHandler;
59import org.onosproject.dhcprelay.api.DhcpRelayService;
Yi Tseng919b2df2017-09-07 16:22:51 -070060import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng483ac6f2017-08-02 15:03:31 -070061import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
Taras Lemkin371ac5e2018-03-26 14:52:58 +000062import org.onosproject.dhcprelay.config.DhcpServerConfig;
Kalhee Kimba366062017-11-07 16:32:09 +000063import org.onosproject.dhcprelay.config.EnableDhcpFpmConfig;
Anjali K Kf3963f12019-04-16 19:27:30 +000064import org.onosproject.dhcprelay.config.HostAutoRelearnConfig;
Taras Lemkin371ac5e2018-03-26 14:52:58 +000065import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
66import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070067import org.onosproject.dhcprelay.store.DhcpRecord;
68import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimba366062017-11-07 16:32:09 +000069import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
70import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng127ffe52017-09-12 15:55:17 -070071import org.onosproject.net.Device;
Yi Tseng4f2a0462017-08-31 11:21:00 -070072import org.onosproject.net.Host;
Yi Tseng483ac6f2017-08-02 15:03:31 -070073import org.onosproject.net.config.Config;
Yi Tseng127ffe52017-09-12 15:55:17 -070074import org.onosproject.net.device.DeviceEvent;
75import org.onosproject.net.device.DeviceListener;
76import org.onosproject.net.device.DeviceService;
Ray Milkeyfacf2862017-08-03 11:58:29 -070077import org.onosproject.net.intf.Interface;
78import org.onosproject.net.intf.InterfaceService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070079import org.onosproject.net.ConnectPoint;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070080import org.onosproject.net.HostId;
Anjali K Kf3963f12019-04-16 19:27:30 +000081import org.onosproject.net.HostLocation;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070082import org.onosproject.net.config.ConfigFactory;
83import org.onosproject.net.config.NetworkConfigEvent;
84import org.onosproject.net.config.NetworkConfigListener;
85import org.onosproject.net.config.NetworkConfigRegistry;
86import org.onosproject.net.flow.DefaultTrafficSelector;
87import org.onosproject.net.flow.DefaultTrafficTreatment;
88import org.onosproject.net.flow.TrafficSelector;
89import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070090import org.onosproject.net.host.HostService;
Anjali K Kf3963f12019-04-16 19:27:30 +000091import org.onosproject.net.Port;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070092import org.onosproject.net.packet.DefaultOutboundPacket;
93import org.onosproject.net.packet.OutboundPacket;
94import org.onosproject.net.packet.PacketContext;
95import org.onosproject.net.packet.PacketPriority;
96import org.onosproject.net.packet.PacketProcessor;
97import org.onosproject.net.packet.PacketService;
Kalhee Kimba366062017-11-07 16:32:09 +000098
Yi Tseng7a38f9a2017-06-09 14:36:40 -070099import org.osgi.service.component.ComponentContext;
100import org.slf4j.Logger;
101import org.slf4j.LoggerFactory;
Kalhee Kime5bb7092017-11-29 19:03:02 +0000102import java.util.concurrent.Executors;
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000103import java.util.concurrent.ExecutorService;
Kalhee Kime5bb7092017-11-29 19:03:02 +0000104import java.util.concurrent.ScheduledExecutorService;
105import java.util.concurrent.TimeUnit;
Anjali K Kf3963f12019-04-16 19:27:30 +0000106import java.util.concurrent.CopyOnWriteArraySet;
107
Kalhee Kime5bb7092017-11-29 19:03:02 +0000108import static org.onlab.util.Tools.groupedThreads;
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000109import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700110import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
Yi Tseng127ffe52017-09-12 15:55:17 -0700111
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700112/**
113 * DHCP Relay Agent Application Component.
114 */
115@Component(immediate = true)
116@Service
117public class DhcpRelayManager implements DhcpRelayService {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700118 public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
Charles Chanc1daa8f2019-01-23 15:03:17 -0800119 public static final String ROUTE_STORE_IMPL = "org.onosproject.routeservice.store.RouteStoreImpl";
Charles Chanc1daa8f2019-01-23 15:03:17 -0800120 private static final int DEFAULT_POOL_SIZE = 32;
Yi Tseng525ff402017-10-23 19:39:39 -0700121
Yi Tseng51f1be92017-09-01 17:24:57 -0700122 private static final TrafficSelector ARP_SELECTOR = DefaultTrafficSelector.builder()
123 .matchEthType(Ethernet.TYPE_ARP)
124 .build();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700125 private final Logger log = LoggerFactory.getLogger(getClass());
126 private final InternalConfigListener cfgListener = new InternalConfigListener();
Anjali K Kf3963f12019-04-16 19:27:30 +0000127 protected CopyOnWriteArraySet hostAutoRelearnEnabledDevices = new CopyOnWriteArraySet();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700128
129 private final Set<ConfigFactory> factories = ImmutableSet.of(
Yi Tseng483ac6f2017-08-02 15:03:31 -0700130 new ConfigFactory<ApplicationId, DefaultDhcpRelayConfig>(APP_SUBJECT_FACTORY,
Kalhee Kimba366062017-11-07 16:32:09 +0000131 DefaultDhcpRelayConfig.class,
132 DefaultDhcpRelayConfig.KEY,
133 true) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700134 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700135 public DefaultDhcpRelayConfig createConfig() {
136 return new DefaultDhcpRelayConfig();
137 }
138 },
139 new ConfigFactory<ApplicationId, IndirectDhcpRelayConfig>(APP_SUBJECT_FACTORY,
Kalhee Kimba366062017-11-07 16:32:09 +0000140 IndirectDhcpRelayConfig.class,
141 IndirectDhcpRelayConfig.KEY,
142 true) {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700143 @Override
144 public IndirectDhcpRelayConfig createConfig() {
145 return new IndirectDhcpRelayConfig();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700146 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700147 },
148 new ConfigFactory<ApplicationId, IgnoreDhcpConfig>(APP_SUBJECT_FACTORY,
Kalhee Kimba366062017-11-07 16:32:09 +0000149 IgnoreDhcpConfig.class,
150 IgnoreDhcpConfig.KEY,
151 true) {
Yi Tseng51f1be92017-09-01 17:24:57 -0700152 @Override
153 public IgnoreDhcpConfig createConfig() {
154 return new IgnoreDhcpConfig();
155 }
Kalhee Kimba366062017-11-07 16:32:09 +0000156 },
157 new ConfigFactory<ApplicationId, EnableDhcpFpmConfig>(APP_SUBJECT_FACTORY,
158 EnableDhcpFpmConfig.class,
159 EnableDhcpFpmConfig.KEY,
160 false) {
161 @Override
162 public EnableDhcpFpmConfig createConfig() {
163 return new EnableDhcpFpmConfig();
164 }
Anjali K Kf3963f12019-04-16 19:27:30 +0000165 },
166 new ConfigFactory<ApplicationId, HostAutoRelearnConfig>(APP_SUBJECT_FACTORY,
167 HostAutoRelearnConfig.class,
168 HostAutoRelearnConfig.KEY,
169 true) {
170 @Override
171 public HostAutoRelearnConfig createConfig() {
172 return new HostAutoRelearnConfig();
173 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700174 }
175 );
176
Kalhee Kime5bb7092017-11-29 19:03:02 +0000177
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700178 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
179 protected NetworkConfigRegistry cfgService;
180
181 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
182 protected CoreService coreService;
183
184 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
185 protected PacketService packetService;
186
187 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
188 protected HostService hostService;
189
190 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700191 protected InterfaceService interfaceService;
192
193 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
194 protected DhcpRelayStore dhcpRelayStore;
195
196 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
197 protected ComponentConfigService compCfgService;
198
Yi Tseng51f1be92017-09-01 17:24:57 -0700199 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng127ffe52017-09-12 15:55:17 -0700200 protected DeviceService deviceService;
201
Kalhee Kimba366062017-11-07 16:32:09 +0000202 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
203 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
204
Yi Tseng51301292017-07-28 13:02:59 -0700205 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
Yi Tseng127ffe52017-09-12 15:55:17 -0700206 target = "(version=4)")
Yi Tseng51301292017-07-28 13:02:59 -0700207 protected DhcpHandler v4Handler;
208
209 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
Yi Tseng127ffe52017-09-12 15:55:17 -0700210 target = "(version=6)")
Yi Tseng51301292017-07-28 13:02:59 -0700211 protected DhcpHandler v6Handler;
212
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700213 @Property(name = "arpEnabled", boolValue = true,
Yi Tseng127ffe52017-09-12 15:55:17 -0700214 label = "Enable Address resolution protocol")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700215 protected boolean arpEnabled = true;
216
Kalhee Kime5bb7092017-11-29 19:03:02 +0000217 @Property(name = "dhcpPollInterval", intValue = 24 * 3600,
218 label = "dhcp relay poll interval")
219 protected int dhcpPollInterval = 24 * 3600;
220
Kalhee Kimba366062017-11-07 16:32:09 +0000221 @Property(name = "dhcpFpmEnabled", boolValue = false,
222 label = "Enable DhcpRelay Fpm")
223 protected boolean dhcpFpmEnabled = false;
224
Anjali K Kf3963f12019-04-16 19:27:30 +0000225 @Property(name = "dhcpHostRelearnProbeInterval", intValue = 500,
226 label = "dhcp host relearn probe interval in millis")
227 protected int dhcpHostRelearnProbeInterval = 500;
228
229 @Property(name = "dhcpHostRelearnProbeCount", intValue = 3,
230 label = "dhcp host relearn probe count")
231 protected int dhcpHostRelearnProbeCount = 3;
232
Kalhee Kime5bb7092017-11-29 19:03:02 +0000233 private ScheduledExecutorService timerExecutor;
Anjali K Kf3963f12019-04-16 19:27:30 +0000234 private ScheduledExecutorService executorService = null;
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000235 protected ExecutorService devEventExecutor;
Charles Chanc1daa8f2019-01-23 15:03:17 -0800236 private ExecutorService packetExecutor;
Kalhee Kime5bb7092017-11-29 19:03:02 +0000237
Yi Tseng127ffe52017-09-12 15:55:17 -0700238 protected DeviceListener deviceListener = new InternalDeviceListener();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700239 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700240 private ApplicationId appId;
241
Anjali K Kf3963f12019-04-16 19:27:30 +0000242 private static final int POOL_SIZE = 10;
243 private static final int HOST_PROBE_INIT_DELAY = 500;
244
Kalhee Kime5bb7092017-11-29 19:03:02 +0000245 /**
246 * One second timer.
247 */
248 class Dhcp6Timer implements Runnable {
249 @Override
250 public void run() {
251 v6Handler.timeTick();
252 }
253 };
Kalhee Kimba366062017-11-07 16:32:09 +0000254
Anjali K Kf3963f12019-04-16 19:27:30 +0000255
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700256 @Activate
257 protected void activate(ComponentContext context) {
258 //start the dhcp relay agent
259 appId = coreService.registerApplication(DHCP_RELAY_APP);
260
261 cfgService.addListener(cfgListener);
262 factories.forEach(cfgService::registerConfigFactory);
263 //update the dhcp server configuration.
264 updateConfig();
Yi Tseng51301292017-07-28 13:02:59 -0700265
266 //add the packet processor
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700267 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Yi Tseng51301292017-07-28 13:02:59 -0700268
Kalhee Kime5bb7092017-11-29 19:03:02 +0000269 timerExecutor = Executors.newScheduledThreadPool(1,
Charles Chanc1daa8f2019-01-23 15:03:17 -0800270 groupedThreads("onos/dhcprelay", "config-reloader-%d", log));
271 timerExecutor.scheduleAtFixedRate(new Dhcp6Timer(), 0, dhcpPollInterval, TimeUnit.SECONDS);
272 packetExecutor = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE,
273 groupedThreads("onos/dhcprelay", "packet-%d", log));
Kalhee Kime5bb7092017-11-29 19:03:02 +0000274
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000275 devEventExecutor = newSingleThreadScheduledExecutor(
276 groupedThreads("onos/dhcprelay-dev-events", "events-%d", log));
277
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700278 modified(context);
279
Yi Tseng06799d62017-09-01 16:02:56 -0700280 // Enable distribute route store
281 compCfgService.preSetProperty(ROUTE_STORE_IMPL,
Kalhee Kimba366062017-11-07 16:32:09 +0000282 "distributed", Boolean.TRUE.toString());
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700283 compCfgService.registerProperties(getClass());
Yi Tseng127ffe52017-09-12 15:55:17 -0700284
Anjali K Kf3963f12019-04-16 19:27:30 +0000285 executorService = Executors.newScheduledThreadPool(POOL_SIZE);
286
Yi Tseng127ffe52017-09-12 15:55:17 -0700287 deviceService.addListener(deviceListener);
Kalhee Kime5bb7092017-11-29 19:03:02 +0000288
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700289 log.info("DHCP-RELAY Started");
290 }
291
292 @Deactivate
293 protected void deactivate() {
294 cfgService.removeListener(cfgListener);
295 factories.forEach(cfgService::unregisterConfigFactory);
296 packetService.removeProcessor(dhcpRelayPacketProcessor);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700297 cancelArpPackets();
Yi Tsengdfa8ee22017-08-07 12:45:01 -0700298 compCfgService.unregisterProperties(getClass(), false);
Yi Tseng127ffe52017-09-12 15:55:17 -0700299 deviceService.removeListener(deviceListener);
Kalhee Kime5bb7092017-11-29 19:03:02 +0000300 timerExecutor.shutdown();
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000301 devEventExecutor.shutdownNow();
302 devEventExecutor = null;
Charles Chanc1daa8f2019-01-23 15:03:17 -0800303 packetExecutor.shutdown();
304 timerExecutor = null;
305 packetExecutor = null;
Anjali K Kf3963f12019-04-16 19:27:30 +0000306 executorService.shutdown();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700307 log.info("DHCP-RELAY Stopped");
308 }
309
310 @Modified
311 protected void modified(ComponentContext context) {
312 Dictionary<?, ?> properties = context.getProperties();
313 Boolean flag;
314
315 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
316 if (flag != null) {
317 arpEnabled = flag;
318 log.info("Address resolution protocol is {}",
Kalhee Kimba366062017-11-07 16:32:09 +0000319 arpEnabled ? "enabled" : "disabled");
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700320 }
321
322 if (arpEnabled) {
323 requestArpPackets();
324 } else {
325 cancelArpPackets();
326 }
Kalhee Kimba366062017-11-07 16:32:09 +0000327
Kalhee Kime5bb7092017-11-29 19:03:02 +0000328 int intervalVal = Tools.getIntegerProperty(properties, "dhcpPollInterval");
329 log.info("DhcpRelay poll interval new {} old {}", intervalVal, dhcpPollInterval);
330 if (intervalVal != dhcpPollInterval) {
331 timerExecutor.shutdown();
332 dhcpPollInterval = intervalVal;
333 timerExecutor = Executors.newScheduledThreadPool(1,
334 groupedThreads("dhcpRelay",
335 "config-reloader-%d", log));
336 timerExecutor.scheduleAtFixedRate(new Dhcp6Timer(),
337 0,
338 dhcpPollInterval > 1 ? dhcpPollInterval : 1,
339 TimeUnit.SECONDS);
340 v6Handler.setDhcp6PollInterval(dhcpPollInterval);
341 }
342
Kalhee Kimba366062017-11-07 16:32:09 +0000343 flag = Tools.isPropertyEnabled(properties, "dhcpFpmEnabled");
344 if (flag != null) {
345 boolean oldValue = dhcpFpmEnabled;
346 dhcpFpmEnabled = flag;
347 log.info("DhcpRelay FPM is {}",
348 dhcpFpmEnabled ? "enabled" : "disabled");
349
350 if (dhcpFpmEnabled && !oldValue) {
351 log.info("Dhcp Fpm is enabled.");
352 processDhcpFpmRoutes(true);
353 }
354 if (!dhcpFpmEnabled && oldValue) {
355 log.info("Dhcp Fpm is disabled.");
356 processDhcpFpmRoutes(false);
357 }
358 v6Handler.setDhcpFpmEnabled(dhcpFpmEnabled);
359 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700360 }
361
Yi Tseng525ff402017-10-23 19:39:39 -0700362 private static List<TrafficSelector> buildClientDhcpSelectors() {
363 return Streams.concat(Dhcp4HandlerImpl.DHCP_SELECTORS.stream(),
364 Dhcp6HandlerImpl.DHCP_SELECTORS.stream())
365 .collect(Collectors.toList());
366 }
367
Yi Tseng483ac6f2017-08-02 15:03:31 -0700368 /**
369 * Updates DHCP relay app configuration.
370 */
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700371 private void updateConfig() {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700372 DefaultDhcpRelayConfig defaultConfig =
373 cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
374 IndirectDhcpRelayConfig indirectConfig =
375 cfgService.getConfig(appId, IndirectDhcpRelayConfig.class);
Yi Tseng51f1be92017-09-01 17:24:57 -0700376 IgnoreDhcpConfig ignoreDhcpConfig =
377 cfgService.getConfig(appId, IgnoreDhcpConfig.class);
Anjali K Kf3963f12019-04-16 19:27:30 +0000378 HostAutoRelearnConfig hostAutoRelearnConfig =
379 cfgService.getConfig(appId, HostAutoRelearnConfig.class);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700380
381 if (defaultConfig != null) {
382 updateConfig(defaultConfig);
383 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700384 if (indirectConfig != null) {
385 updateConfig(indirectConfig);
386 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700387 if (ignoreDhcpConfig != null) {
388 updateConfig(ignoreDhcpConfig);
389 }
Anjali K Kf3963f12019-04-16 19:27:30 +0000390 if (hostAutoRelearnConfig != null) {
391 updateConfig(hostAutoRelearnConfig);
392 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700393 }
394
395 /**
396 * Updates DHCP relay app configuration with given configuration.
397 *
398 * @param config the configuration ot update
399 */
Yi Tseng51f1be92017-09-01 17:24:57 -0700400 protected void updateConfig(Config config) {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700401 if (config instanceof IndirectDhcpRelayConfig) {
402 IndirectDhcpRelayConfig indirectConfig = (IndirectDhcpRelayConfig) config;
403 v4Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
404 v6Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700405 } else if (config instanceof DefaultDhcpRelayConfig) {
406 DefaultDhcpRelayConfig defaultConfig = (DefaultDhcpRelayConfig) config;
407 v4Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
408 v6Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
Yi Tseng483ac6f2017-08-02 15:03:31 -0700409 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700410 if (config instanceof IgnoreDhcpConfig) {
Yi Tseng525ff402017-10-23 19:39:39 -0700411 v4Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
412 v6Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
Yi Tseng51f1be92017-09-01 17:24:57 -0700413 }
Anjali K Kf3963f12019-04-16 19:27:30 +0000414 if (config instanceof HostAutoRelearnConfig) {
415 setHostAutoRelearnConfig((HostAutoRelearnConfig) config);
416 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700417 }
418
419 protected void removeConfig(Config config) {
420 if (config instanceof IndirectDhcpRelayConfig) {
421 v4Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
422 v6Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
423 } else if (config instanceof DefaultDhcpRelayConfig) {
424 v4Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
425 v6Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
426 }
427 if (config instanceof IgnoreDhcpConfig) {
Yi Tseng525ff402017-10-23 19:39:39 -0700428 v4Handler.updateIgnoreVlanConfig(null);
429 v6Handler.updateIgnoreVlanConfig(null);
Yi Tseng51f1be92017-09-01 17:24:57 -0700430 }
431 }
432
Kalhee Kimba366062017-11-07 16:32:09 +0000433 private void processDhcpFpmRoutes(Boolean add) {
434 // needs to restore/remove fpm
435 }
436
437 public boolean isDhcpFpmEnabled() {
438 return dhcpFpmEnabled;
439 }
440
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700441 /**
442 * Request ARP packet in via PacketService.
443 */
444 private void requestArpPackets() {
Yi Tseng51f1be92017-09-01 17:24:57 -0700445 packetService.requestPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700446 }
447
448 /**
449 * Cancel requested ARP packets in via packet service.
450 */
451 private void cancelArpPackets() {
Yi Tseng51f1be92017-09-01 17:24:57 -0700452 packetService.cancelPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700453 }
454
455 @Override
456 public Optional<DhcpRecord> getDhcpRecord(HostId hostId) {
457 return dhcpRelayStore.getDhcpRecord(hostId);
458 }
459
460 @Override
461 public Collection<DhcpRecord> getDhcpRecords() {
462 return dhcpRelayStore.getDhcpRecords();
463 }
Anjali K Kf3963f12019-04-16 19:27:30 +0000464
Kalhee Kime5bb7092017-11-29 19:03:02 +0000465 @Override
466 public void updateDhcpRecord(HostId hostId, DhcpRecord dhcpRecord) {
467 dhcpRelayStore.updateDhcpRecord(hostId, dhcpRecord);
468 }
Anjali K Kf3963f12019-04-16 19:27:30 +0000469
Yi Tseng13a41a12017-07-26 13:45:01 -0700470 @Override
471 public Optional<MacAddress> getDhcpServerMacAddress() {
Yi Tseng4f2a0462017-08-31 11:21:00 -0700472 // TODO: depreated it
473 DefaultDhcpRelayConfig config = cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
474 DhcpServerConfig serverConfig = config.dhcpServerConfigs().get(0);
475 Ip4Address serverip = serverConfig.getDhcpServerIp4().get();
476 return hostService.getHostsByIp(serverip)
477 .stream()
478 .map(Host::mac)
479 .findFirst();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700480 }
481
Yi Tseng919b2df2017-09-07 16:22:51 -0700482 @Override
483 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
484 return ImmutableList.<DhcpServerInfo>builder()
485 .addAll(v4Handler.getDefaultDhcpServerInfoList())
486 .addAll(v6Handler.getDefaultDhcpServerInfoList())
487 .build();
488 }
489
490 @Override
491 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
492 return ImmutableList.<DhcpServerInfo>builder()
493 .addAll(v4Handler.getIndirectDhcpServerInfoList())
494 .addAll(v6Handler.getIndirectDhcpServerInfoList())
495 .build();
496 }
497
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700498 /**
499 * Gets DHCP data from a packet.
500 *
501 * @param packet the packet
502 * @return the DHCP data; empty if it is not a DHCP packet
503 */
504 private Optional<DHCP> findDhcp(Ethernet packet) {
505 return Stream.of(packet)
506 .filter(Objects::nonNull)
507 .map(Ethernet::getPayload)
508 .filter(p -> p instanceof IPv4)
509 .map(IPacket::getPayload)
510 .filter(Objects::nonNull)
511 .filter(p -> p instanceof UDP)
512 .map(IPacket::getPayload)
513 .filter(Objects::nonNull)
514 .filter(p -> p instanceof DHCP)
515 .map(p -> (DHCP) p)
516 .findFirst();
517 }
518
519 /**
520 * Gets DHCPv6 data from a packet.
521 *
522 * @param packet the packet
523 * @return the DHCPv6 data; empty if it is not a DHCPv6 packet
524 */
525 private Optional<DHCP6> findDhcp6(Ethernet packet) {
526 return Stream.of(packet)
527 .filter(Objects::nonNull)
528 .map(Ethernet::getPayload)
529 .filter(p -> p instanceof IPv6)
530 .map(IPacket::getPayload)
531 .filter(Objects::nonNull)
532 .filter(p -> p instanceof UDP)
533 .map(IPacket::getPayload)
534 .filter(Objects::nonNull)
535 .filter(p -> p instanceof DHCP6)
536 .map(p -> (DHCP6) p)
537 .findFirst();
538 }
539
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700540
541 private class DhcpRelayPacketProcessor implements PacketProcessor {
Anjali K Kf3963f12019-04-16 19:27:30 +0000542
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700543 @Override
544 public void process(PacketContext context) {
Charles Chanc1daa8f2019-01-23 15:03:17 -0800545 packetExecutor.execute(() -> processInternal(context));
546 }
547
548 private void processInternal(PacketContext context) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700549 // process the packet and get the payload
550 Ethernet packet = context.inPacket().parsed();
551 if (packet == null) {
552 return;
553 }
554
555 findDhcp(packet).ifPresent(dhcpPayload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700556 v4Handler.processDhcpPacket(context, dhcpPayload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700557 });
558
559 findDhcp6(packet).ifPresent(dhcp6Payload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700560 v6Handler.processDhcpPacket(context, dhcp6Payload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700561 });
562
563 if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
564 ARP arpPacket = (ARP) packet.getPayload();
565 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
566 Set<Interface> interfaces = interfaceService.
567 getInterfacesByPort(context.inPacket().receivedFrom());
568 //ignore the packets if dhcp server interface is not configured on onos.
569 if (interfaces.isEmpty()) {
570 log.warn("server virtual interface not configured");
571 return;
572 }
573 if ((arpPacket.getOpCode() != ARP.OP_REQUEST)) {
574 // handle request only
575 return;
576 }
577 MacAddress interfaceMac = interfaces.stream()
578 .filter(iface -> iface.vlan().equals(vlanId))
579 .map(Interface::mac)
580 .filter(mac -> !mac.equals(MacAddress.NONE))
581 .findFirst()
Yi Tseng51301292017-07-28 13:02:59 -0700582 .orElse(MacAddress.ONOS);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700583 if (interfaceMac == null) {
584 // can't find interface mac address
585 return;
586 }
587 processArpPacket(context, packet, interfaceMac);
588 }
589 }
590
591 /**
592 * Processes the ARP Payload and initiates a reply to the client.
593 *
594 * @param context the packet context
595 * @param packet the ethernet payload
596 * @param replyMac mac address to be replied
597 */
598 private void processArpPacket(PacketContext context, Ethernet packet, MacAddress replyMac) {
599 ARP arpPacket = (ARP) packet.getPayload();
Ray Milkeyf0c47612017-09-28 11:29:38 -0700600 ARP arpReply = arpPacket.duplicate();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700601 arpReply.setOpCode(ARP.OP_REPLY);
602
603 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
604 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
605 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
606 arpReply.setSenderHardwareAddress(replyMac.toBytes());
607
608 // Ethernet Frame.
609 Ethernet ethReply = new Ethernet();
610 ethReply.setSourceMACAddress(replyMac.toBytes());
611 ethReply.setDestinationMACAddress(packet.getSourceMAC());
612 ethReply.setEtherType(Ethernet.TYPE_ARP);
613 ethReply.setVlanID(packet.getVlanID());
614 ethReply.setPayload(arpReply);
615
616 ConnectPoint targetPort = context.inPacket().receivedFrom();
617 TrafficTreatment t = DefaultTrafficTreatment.builder()
618 .setOutput(targetPort.port()).build();
619 OutboundPacket o = new DefaultOutboundPacket(
620 targetPort.deviceId(), t, ByteBuffer.wrap(ethReply.serialize()));
621 if (log.isTraceEnabled()) {
622 log.trace("Relaying ARP packet {} to {}", packet, targetPort);
623 }
624 packetService.emit(o);
625 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700626 }
627
628 /**
629 * Listener for network config events.
630 */
631 private class InternalConfigListener implements NetworkConfigListener {
632 @Override
633 public void event(NetworkConfigEvent event) {
Yi Tseng51f1be92017-09-01 17:24:57 -0700634 switch (event.type()) {
635 case CONFIG_UPDATED:
636 case CONFIG_ADDED:
637 event.config().ifPresent(config -> {
638 updateConfig(config);
639 log.info("{} updated", config.getClass().getSimpleName());
640 });
641 break;
642 case CONFIG_REMOVED:
643 event.prevConfig().ifPresent(config -> {
644 removeConfig(config);
645 log.info("{} removed", config.getClass().getSimpleName());
646 });
647 break;
Charles Chan6be77d82018-04-21 00:44:29 -0700648 case CONFIG_REGISTERED:
649 case CONFIG_UNREGISTERED:
650 break;
Yi Tseng51f1be92017-09-01 17:24:57 -0700651 default:
652 log.warn("Unsupported event type {}", event.type());
653 break;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700654 }
Charles Chan285cb772018-01-03 16:26:32 -0800655 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700656
Charles Chan285cb772018-01-03 16:26:32 -0800657 @Override
658 public boolean isRelevant(NetworkConfigEvent event) {
659 if (event.configClass().equals(DefaultDhcpRelayConfig.class) ||
660 event.configClass().equals(IndirectDhcpRelayConfig.class) ||
Anjali K Kf3963f12019-04-16 19:27:30 +0000661 event.configClass().equals(IgnoreDhcpConfig.class) ||
662 event.configClass().equals(HostAutoRelearnConfig.class)) {
Charles Chan285cb772018-01-03 16:26:32 -0800663 return true;
664 }
665 log.debug("Ignore irrelevant event class {}", event.configClass().getName());
666 return false;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700667 }
668 }
Yi Tseng127ffe52017-09-12 15:55:17 -0700669
670 private class InternalDeviceListener implements DeviceListener {
671
672 @Override
673 public void event(DeviceEvent event) {
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000674 if (devEventExecutor != null) {
Anjali K Kf3963f12019-04-16 19:27:30 +0000675 final Device device = event.subject();
Yi Tseng127ffe52017-09-12 15:55:17 -0700676 switch (event.type()) {
677 case DEVICE_ADDED:
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000678 devEventExecutor.execute(this::updateIgnoreVlanConfigs);
Yi Tseng127ffe52017-09-12 15:55:17 -0700679 break;
680 case DEVICE_AVAILABILITY_CHANGED:
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000681 devEventExecutor.execute(() -> deviceAvailabilityChanged(device));
682 break;
Anjali K Kf3963f12019-04-16 19:27:30 +0000683 case PORT_UPDATED:
684 Port port = event.port();
685 devEventExecutor.execute(() -> portUpdatedEventHandler(device, port));
686 break;
Yi Tseng127ffe52017-09-12 15:55:17 -0700687 default:
688 break;
689 }
Ruchi Sahota196a9ca2019-03-01 16:56:07 +0000690 }
Yi Tseng127ffe52017-09-12 15:55:17 -0700691 }
692
Anjali K Kf3963f12019-04-16 19:27:30 +0000693 private void portUpdatedEventHandler(Device device, Port port) {
694 if (hostAutoRelearnEnabledDevices.contains(device.id()) && port.isEnabled()) {
695 ConnectPoint cp = new ConnectPoint(device.id(), port.number());
696 HostLocation hostLocation = new HostLocation(cp, 0);
697 Set<DhcpRecord> records = dhcpRelayStore.getDhcpRecords()
698 .stream()
699 .filter(i -> i.directlyConnected())
700 .filter(i -> i.locations().contains(hostLocation))
701 .collect(Collectors.toSet());
702
703 for (DhcpRecord i : records) {
704 //found a dhcprecord matching the connect point of the port event
705 log.debug("portUpdatedEventHandler : DHCP record {}, sending message on CP {} Mac {} Vlan{}",
706 i, cp, i.macAddress(), i.vlanId());
707 if (i.ip4Address().isPresent()) {
708 log.warn("Sending host relearn probe for v4 not supported for Mac {} Vlan{} ip {}",
709 i.macAddress(), i.vlanId(), i.ip4Address());
710 } else if (i.ip6Address().isPresent()) {
711 sendHostRelearnProbe(cp, i.macAddress(), i.vlanId(), i.ip6Address());
712 }
713 }
714 }
715 }
716
Yi Tseng127ffe52017-09-12 15:55:17 -0700717 private void deviceAvailabilityChanged(Device device) {
718 if (deviceService.isAvailable(device.id())) {
Yi Tseng525ff402017-10-23 19:39:39 -0700719 updateIgnoreVlanConfigs();
Saurav Dasfa9004b2017-12-13 16:19:35 -0800720 } else {
721 removeIgnoreVlanState();
Yi Tseng127ffe52017-09-12 15:55:17 -0700722 }
723 }
724
Yi Tseng525ff402017-10-23 19:39:39 -0700725 private void updateIgnoreVlanConfigs() {
Yi Tseng127ffe52017-09-12 15:55:17 -0700726 IgnoreDhcpConfig config = cfgService.getConfig(appId, IgnoreDhcpConfig.class);
Yi Tseng525ff402017-10-23 19:39:39 -0700727 v4Handler.updateIgnoreVlanConfig(config);
728 v6Handler.updateIgnoreVlanConfig(config);
Yi Tseng127ffe52017-09-12 15:55:17 -0700729 }
Saurav Dasfa9004b2017-12-13 16:19:35 -0800730
731 private void removeIgnoreVlanState() {
732 IgnoreDhcpConfig config = cfgService.getConfig(appId, IgnoreDhcpConfig.class);
733 v4Handler.removeIgnoreVlanState(config);
734 v6Handler.removeIgnoreVlanState(config);
735 }
Yi Tseng127ffe52017-09-12 15:55:17 -0700736 }
Kalhee Kimba366062017-11-07 16:32:09 +0000737
Anjali K Kf3963f12019-04-16 19:27:30 +0000738 private void setHostAutoRelearnConfig(HostAutoRelearnConfig config) {
739 hostAutoRelearnEnabledDevices.clear();
740 if (config == null) {
741 return;
742 }
743 hostAutoRelearnEnabledDevices.addAll(config.hostAutoRelearnEnabledDevices());
744 }
745
746 // Packet transmission class.
747 private class PktTransmitter implements Runnable {
748
749 MacAddress mac;
750 VlanId vlanId;
751 Ip6Address ipv6Address;
752 ConnectPoint connectPoint;
753
754 PktTransmitter(MacAddress mac, VlanId vlanId, Ip6Address ipv6Address, ConnectPoint connectPoint) {
755 this.mac = mac;
756 this.vlanId = vlanId;
757 this.ipv6Address = ipv6Address;
758 this.connectPoint = connectPoint;
759 }
760
761 @Override
762 public void run() {
763 log.debug("Host Relearn probe packet transmission activated for Mac {} Vlan {} Ip {} ConnectPt {}",
764 mac, vlanId, ipv6Address, connectPoint);
765 if (mac == null || vlanId == null || ipv6Address == null || connectPoint == null) {
766 return;
767 }
768
769 byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
770 byte[] senderIpAddress = new byte[Ip6Address.BYTE_LENGTH];
771 Ethernet ethernet = NeighborSolicitation.buildNdpSolicit(
772 this.ipv6Address,
773 Ip6Address.valueOf(senderIpAddress),
774 this.ipv6Address, //destip
775 MacAddress.valueOf(senderMacAddress),
776 this.mac,
777 this.vlanId);
778
779 sendHostRelearnProbeToConnectPoint(ethernet, connectPoint);
780
781 log.debug("Host Relearn Probe transmission completed.");
782 }
783 }
784
785 //Create packet and schedule transmitter thread.
786 private void sendHostRelearnProbe(ConnectPoint connectPoint, MacAddress mac, VlanId vlanId,
787 Optional<Ip6Address> ipv6Address) {
788 PktTransmitter nsTransmitter = new PktTransmitter(mac, vlanId, ipv6Address.get(), connectPoint);
789 executorService.schedule(nsTransmitter, HOST_PROBE_INIT_DELAY, TimeUnit.MILLISECONDS);
790 }
791
792 // Send Host Relearn Probe packets to ConnectPoint
793 private void sendHostRelearnProbeToConnectPoint(Ethernet nsPacket, ConnectPoint connectPoint) {
794 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
795 OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
796 treatment, ByteBuffer.wrap(nsPacket.serialize()));
797 int counter = 0;
798 try {
799 while (counter < dhcpHostRelearnProbeCount) {
800 packetService.emit(outboundPacket);
801 counter++;
802 Thread.sleep(dhcpHostRelearnProbeInterval);
803 }
804 } catch (Exception e) {
805 log.error("Exception while emmiting packet {}", e.getMessage(), e);
806 }
807 }
Kalhee Kimba366062017-11-07 16:32:09 +0000808
809
810 public Optional<FpmRecord> getFpmRecord(IpPrefix prefix) {
811 return dhcpFpmPrefixStore.getFpmRecord(prefix);
812 }
813
814 public Collection<FpmRecord> getFpmRecords() {
815 return dhcpFpmPrefixStore.getFpmRecords();
816 }
817
818 @Override
819 public void addFpmRecord(IpPrefix prefix, FpmRecord fpmRecord) {
820 dhcpFpmPrefixStore.addFpmRecord(prefix, fpmRecord);
821 }
822
823 @Override
824 public Optional<FpmRecord> removeFpmRecord(IpPrefix prefix) {
825 return dhcpFpmPrefixStore.removeFpmRecord(prefix);
826 }
Kalhee Kime5bb7092017-11-29 19:03:02 +0000827
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700828}