blob: a8541a679991663c8ab1debeb16d6678d941b601 [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
Yi Tseng51f1be92017-09-01 17:24:57 -070018import com.google.common.collect.ImmutableList;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import com.google.common.collect.ImmutableSet;
Yi Tseng525ff402017-10-23 19:39:39 -070020import com.google.common.collect.Streams;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070021import org.onlab.packet.ARP;
22import org.onlab.packet.DHCP;
23import org.onlab.packet.DHCP6;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070024import org.onlab.packet.Ethernet;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070025import org.onlab.packet.IPacket;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070026import org.onlab.packet.IPv4;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070027import org.onlab.packet.IPv6;
Yi Tseng4f2a0462017-08-31 11:21:00 -070028import org.onlab.packet.Ip4Address;
Kalhee Kimba366062017-11-07 16:32:09 +000029import org.onlab.packet.IpPrefix;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070030import org.onlab.packet.MacAddress;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070031import org.onlab.packet.UDP;
32import org.onlab.packet.VlanId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070033import org.onlab.util.Tools;
34import org.onosproject.cfg.ComponentConfigService;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onosproject.dhcprelay.api.DhcpHandler;
38import org.onosproject.dhcprelay.api.DhcpRelayService;
Yi Tseng919b2df2017-09-07 16:22:51 -070039import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng483ac6f2017-08-02 15:03:31 -070040import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
Taras Lemkin96a0d342018-03-26 14:52:58 +000041import org.onosproject.dhcprelay.config.DhcpServerConfig;
Kalhee Kimba366062017-11-07 16:32:09 +000042import org.onosproject.dhcprelay.config.EnableDhcpFpmConfig;
Taras Lemkin96a0d342018-03-26 14:52:58 +000043import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
45import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070046import org.onosproject.dhcprelay.store.DhcpRecord;
47import org.onosproject.dhcprelay.store.DhcpRelayStore;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070048import org.onosproject.net.ConnectPoint;
Yi Tseng127ffe52017-09-12 15:55:17 -070049import org.onosproject.net.Device;
Yi Tseng4f2a0462017-08-31 11:21:00 -070050import org.onosproject.net.Host;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070051import org.onosproject.net.HostId;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070052import org.onosproject.net.config.Config;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070053import org.onosproject.net.config.ConfigFactory;
54import org.onosproject.net.config.NetworkConfigEvent;
55import org.onosproject.net.config.NetworkConfigListener;
56import org.onosproject.net.config.NetworkConfigRegistry;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070057import org.onosproject.net.device.DeviceEvent;
58import org.onosproject.net.device.DeviceListener;
59import org.onosproject.net.device.DeviceService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070060import org.onosproject.net.flow.DefaultTrafficSelector;
61import org.onosproject.net.flow.DefaultTrafficTreatment;
62import org.onosproject.net.flow.TrafficSelector;
63import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070064import org.onosproject.net.host.HostService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065import org.onosproject.net.intf.Interface;
66import org.onosproject.net.intf.InterfaceService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070067import org.onosproject.net.packet.DefaultOutboundPacket;
68import org.onosproject.net.packet.OutboundPacket;
69import org.onosproject.net.packet.PacketContext;
70import org.onosproject.net.packet.PacketPriority;
71import org.onosproject.net.packet.PacketProcessor;
72import org.onosproject.net.packet.PacketService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070074import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070075import org.osgi.service.component.annotations.Activate;
76import org.osgi.service.component.annotations.Component;
77import org.osgi.service.component.annotations.Deactivate;
78import org.osgi.service.component.annotations.Modified;
79import org.osgi.service.component.annotations.Reference;
80import org.osgi.service.component.annotations.ReferenceCardinality;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070081import org.slf4j.Logger;
82import org.slf4j.LoggerFactory;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070083
84import java.nio.ByteBuffer;
85import java.util.Collection;
86import java.util.Collections;
87import java.util.Dictionary;
88import java.util.List;
89import java.util.Objects;
90import java.util.Optional;
91import java.util.Set;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000092import java.util.concurrent.Executors;
Ruchi Sahotae4934e12019-03-01 16:56:07 +000093import java.util.concurrent.ExecutorService;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000094import java.util.concurrent.ScheduledExecutorService;
95import java.util.concurrent.TimeUnit;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096import java.util.stream.Collectors;
97import java.util.stream.Stream;
98
Kalhee Kimd94ceea2017-11-29 19:03:02 +000099import static org.onlab.util.Tools.groupedThreads;
Ray Milkey687c00c2018-10-31 10:18:41 -0700100import static org.onosproject.dhcprelay.OsgiPropertyConstants.ARP_ENABLED;
101import static org.onosproject.dhcprelay.OsgiPropertyConstants.ARP_ENABLED_DEFAULT;
102import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_FPM_ENABLED;
103import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_FPM_ENABLED_DEFAULT;
104import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_POLL_INTERVAL;
105import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_POLL_INTERVAL_DEFAULT;
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000106import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700107import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
Yi Tseng127ffe52017-09-12 15:55:17 -0700108
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700109/**
110 * DHCP Relay Agent Application Component.
111 */
Ray Milkey687c00c2018-10-31 10:18:41 -0700112@Component(
113 immediate = true,
114 service = DhcpRelayService.class,
115 property = {
116 ARP_ENABLED + ":Boolean=" + ARP_ENABLED_DEFAULT,
117 DHCP_POLL_INTERVAL + ":Integer=" + DHCP_POLL_INTERVAL_DEFAULT,
118 DHCP_FPM_ENABLED + ":Boolean=" + DHCP_FPM_ENABLED_DEFAULT
119 }
120)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700121public class DhcpRelayManager implements DhcpRelayService {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700122 public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
Charles Chan64a1c8e2019-01-23 15:03:17 -0800123 public static final String ROUTE_STORE_IMPL = "org.onosproject.routeservice.store.RouteStoreImpl";
124
125 private static final int DEFAULT_POOL_SIZE = 32;
Yi Tseng525ff402017-10-23 19:39:39 -0700126
Yi Tseng51f1be92017-09-01 17:24:57 -0700127 private static final TrafficSelector ARP_SELECTOR = DefaultTrafficSelector.builder()
128 .matchEthType(Ethernet.TYPE_ARP)
129 .build();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700130 private final Logger log = LoggerFactory.getLogger(getClass());
131 private final InternalConfigListener cfgListener = new InternalConfigListener();
132
133 private final Set<ConfigFactory> factories = ImmutableSet.of(
Yi Tseng483ac6f2017-08-02 15:03:31 -0700134 new ConfigFactory<ApplicationId, DefaultDhcpRelayConfig>(APP_SUBJECT_FACTORY,
Kalhee Kimba366062017-11-07 16:32:09 +0000135 DefaultDhcpRelayConfig.class,
136 DefaultDhcpRelayConfig.KEY,
137 true) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700138 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700139 public DefaultDhcpRelayConfig createConfig() {
140 return new DefaultDhcpRelayConfig();
141 }
142 },
143 new ConfigFactory<ApplicationId, IndirectDhcpRelayConfig>(APP_SUBJECT_FACTORY,
Kalhee Kimba366062017-11-07 16:32:09 +0000144 IndirectDhcpRelayConfig.class,
145 IndirectDhcpRelayConfig.KEY,
146 true) {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700147 @Override
148 public IndirectDhcpRelayConfig createConfig() {
149 return new IndirectDhcpRelayConfig();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700150 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700151 },
152 new ConfigFactory<ApplicationId, IgnoreDhcpConfig>(APP_SUBJECT_FACTORY,
Kalhee Kimba366062017-11-07 16:32:09 +0000153 IgnoreDhcpConfig.class,
154 IgnoreDhcpConfig.KEY,
155 true) {
Yi Tseng51f1be92017-09-01 17:24:57 -0700156 @Override
157 public IgnoreDhcpConfig createConfig() {
158 return new IgnoreDhcpConfig();
159 }
Kalhee Kimba366062017-11-07 16:32:09 +0000160 },
161 new ConfigFactory<ApplicationId, EnableDhcpFpmConfig>(APP_SUBJECT_FACTORY,
162 EnableDhcpFpmConfig.class,
163 EnableDhcpFpmConfig.KEY,
164 false) {
165 @Override
166 public EnableDhcpFpmConfig createConfig() {
167 return new EnableDhcpFpmConfig();
168 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700169 }
170 );
171
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700174 protected NetworkConfigRegistry cfgService;
175
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700177 protected CoreService coreService;
178
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700180 protected PacketService packetService;
181
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700183 protected HostService hostService;
184
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700186 protected InterfaceService interfaceService;
187
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700189 protected DhcpRelayStore dhcpRelayStore;
190
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700191 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700192 protected ComponentConfigService compCfgService;
193
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700194 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng127ffe52017-09-12 15:55:17 -0700195 protected DeviceService deviceService;
196
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700197 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kimba366062017-11-07 16:32:09 +0000198 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
199
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700200 @Reference(cardinality = ReferenceCardinality.MANDATORY,
Ray Milkey5504bd22019-03-22 16:24:38 -0700201 target = "(_version=4)")
Yi Tseng51301292017-07-28 13:02:59 -0700202 protected DhcpHandler v4Handler;
203
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700204 @Reference(cardinality = ReferenceCardinality.MANDATORY,
Ray Milkey5504bd22019-03-22 16:24:38 -0700205 target = "(_version=6)")
Yi Tseng51301292017-07-28 13:02:59 -0700206 protected DhcpHandler v6Handler;
207
Ray Milkey687c00c2018-10-31 10:18:41 -0700208 /** Enable Address resolution protocol. */
209 protected boolean arpEnabled = ARP_ENABLED_DEFAULT;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700210
Ray Milkey687c00c2018-10-31 10:18:41 -0700211 /** dhcp relay poll interval. */
212 protected int dhcpPollInterval = DHCP_POLL_INTERVAL_DEFAULT;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000213
Ray Milkey687c00c2018-10-31 10:18:41 -0700214 /** Enable DhcpRelay Fpm. */
215 protected boolean dhcpFpmEnabled = DHCP_FPM_ENABLED_DEFAULT;
Kalhee Kimba366062017-11-07 16:32:09 +0000216
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000217 private ScheduledExecutorService timerExecutor;
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000218 protected ExecutorService devEventExecutor;
Charles Chan64a1c8e2019-01-23 15:03:17 -0800219 private ExecutorService packetExecutor;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000220
Yi Tseng127ffe52017-09-12 15:55:17 -0700221 protected DeviceListener deviceListener = new InternalDeviceListener();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700222 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700223 private ApplicationId appId;
224
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000225 /**
226 * One second timer.
227 */
228 class Dhcp6Timer implements Runnable {
229 @Override
230 public void run() {
231 v6Handler.timeTick();
232 }
233 };
Kalhee Kimba366062017-11-07 16:32:09 +0000234
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700235 @Activate
236 protected void activate(ComponentContext context) {
237 //start the dhcp relay agent
238 appId = coreService.registerApplication(DHCP_RELAY_APP);
239
240 cfgService.addListener(cfgListener);
241 factories.forEach(cfgService::registerConfigFactory);
242 //update the dhcp server configuration.
243 updateConfig();
Yi Tseng51301292017-07-28 13:02:59 -0700244
245 //add the packet processor
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700246 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Yi Tseng51301292017-07-28 13:02:59 -0700247
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000248 timerExecutor = Executors.newScheduledThreadPool(1,
Charles Chan64a1c8e2019-01-23 15:03:17 -0800249 groupedThreads("onos/dhcprelay", "config-reloader-%d", log));
250 timerExecutor.scheduleAtFixedRate(new Dhcp6Timer(), 0, dhcpPollInterval, TimeUnit.SECONDS);
251 packetExecutor = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE,
252 groupedThreads("onos/dhcprelay", "packet-%d", log));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000253
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000254 devEventExecutor = newSingleThreadScheduledExecutor(
255 groupedThreads("onos/dhcprelay-dev-events", "events-%d", log));
256
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700257 modified(context);
258
Yi Tseng06799d62017-09-01 16:02:56 -0700259 // Enable distribute route store
260 compCfgService.preSetProperty(ROUTE_STORE_IMPL,
Kalhee Kimba366062017-11-07 16:32:09 +0000261 "distributed", Boolean.TRUE.toString());
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700262 compCfgService.registerProperties(getClass());
Yi Tseng127ffe52017-09-12 15:55:17 -0700263
264 deviceService.addListener(deviceListener);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000265
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700266 log.info("DHCP-RELAY Started");
267 }
268
269 @Deactivate
270 protected void deactivate() {
271 cfgService.removeListener(cfgListener);
272 factories.forEach(cfgService::unregisterConfigFactory);
273 packetService.removeProcessor(dhcpRelayPacketProcessor);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700274 cancelArpPackets();
Yi Tsengdfa8ee22017-08-07 12:45:01 -0700275 compCfgService.unregisterProperties(getClass(), false);
Yi Tseng127ffe52017-09-12 15:55:17 -0700276 deviceService.removeListener(deviceListener);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000277 timerExecutor.shutdown();
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000278 devEventExecutor.shutdownNow();
279 devEventExecutor = null;
Charles Chan64a1c8e2019-01-23 15:03:17 -0800280 packetExecutor.shutdown();
281 timerExecutor = null;
282 packetExecutor = null;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000283
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700284 log.info("DHCP-RELAY Stopped");
285 }
286
287 @Modified
288 protected void modified(ComponentContext context) {
289 Dictionary<?, ?> properties = context.getProperties();
290 Boolean flag;
291
Ray Milkey687c00c2018-10-31 10:18:41 -0700292 flag = Tools.isPropertyEnabled(properties, ARP_ENABLED);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700293 if (flag != null) {
294 arpEnabled = flag;
295 log.info("Address resolution protocol is {}",
Kalhee Kimba366062017-11-07 16:32:09 +0000296 arpEnabled ? "enabled" : "disabled");
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700297 }
298
299 if (arpEnabled) {
300 requestArpPackets();
301 } else {
302 cancelArpPackets();
303 }
Kalhee Kimba366062017-11-07 16:32:09 +0000304
Ray Milkey687c00c2018-10-31 10:18:41 -0700305 int intervalVal = Tools.getIntegerProperty(properties, DHCP_POLL_INTERVAL);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000306 log.info("DhcpRelay poll interval new {} old {}", intervalVal, dhcpPollInterval);
307 if (intervalVal != dhcpPollInterval) {
308 timerExecutor.shutdown();
309 dhcpPollInterval = intervalVal;
310 timerExecutor = Executors.newScheduledThreadPool(1,
311 groupedThreads("dhcpRelay",
312 "config-reloader-%d", log));
313 timerExecutor.scheduleAtFixedRate(new Dhcp6Timer(),
314 0,
315 dhcpPollInterval > 1 ? dhcpPollInterval : 1,
316 TimeUnit.SECONDS);
317 v6Handler.setDhcp6PollInterval(dhcpPollInterval);
318 }
319
Ray Milkey687c00c2018-10-31 10:18:41 -0700320 flag = Tools.isPropertyEnabled(properties, DHCP_FPM_ENABLED);
Kalhee Kimba366062017-11-07 16:32:09 +0000321 if (flag != null) {
322 boolean oldValue = dhcpFpmEnabled;
323 dhcpFpmEnabled = flag;
324 log.info("DhcpRelay FPM is {}",
325 dhcpFpmEnabled ? "enabled" : "disabled");
326
327 if (dhcpFpmEnabled && !oldValue) {
328 log.info("Dhcp Fpm is enabled.");
329 processDhcpFpmRoutes(true);
330 }
331 if (!dhcpFpmEnabled && oldValue) {
332 log.info("Dhcp Fpm is disabled.");
333 processDhcpFpmRoutes(false);
334 }
335 v6Handler.setDhcpFpmEnabled(dhcpFpmEnabled);
336 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700337 }
338
Yi Tseng525ff402017-10-23 19:39:39 -0700339 private static List<TrafficSelector> buildClientDhcpSelectors() {
340 return Streams.concat(Dhcp4HandlerImpl.DHCP_SELECTORS.stream(),
341 Dhcp6HandlerImpl.DHCP_SELECTORS.stream())
342 .collect(Collectors.toList());
343 }
344
Yi Tseng483ac6f2017-08-02 15:03:31 -0700345 /**
346 * Updates DHCP relay app configuration.
347 */
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700348 private void updateConfig() {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700349 DefaultDhcpRelayConfig defaultConfig =
350 cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
351 IndirectDhcpRelayConfig indirectConfig =
352 cfgService.getConfig(appId, IndirectDhcpRelayConfig.class);
Yi Tseng51f1be92017-09-01 17:24:57 -0700353 IgnoreDhcpConfig ignoreDhcpConfig =
354 cfgService.getConfig(appId, IgnoreDhcpConfig.class);
Yi Tseng483ac6f2017-08-02 15:03:31 -0700355
356 if (defaultConfig != null) {
357 updateConfig(defaultConfig);
358 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700359 if (indirectConfig != null) {
360 updateConfig(indirectConfig);
361 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700362 if (ignoreDhcpConfig != null) {
363 updateConfig(ignoreDhcpConfig);
364 }
Yi Tseng483ac6f2017-08-02 15:03:31 -0700365 }
366
367 /**
368 * Updates DHCP relay app configuration with given configuration.
369 *
370 * @param config the configuration ot update
371 */
Yi Tseng51f1be92017-09-01 17:24:57 -0700372 protected void updateConfig(Config config) {
Yi Tseng483ac6f2017-08-02 15:03:31 -0700373 if (config instanceof IndirectDhcpRelayConfig) {
374 IndirectDhcpRelayConfig indirectConfig = (IndirectDhcpRelayConfig) config;
375 v4Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
376 v6Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
Yi Tseng3df7f9d2017-08-17 13:08:31 -0700377 } else if (config instanceof DefaultDhcpRelayConfig) {
378 DefaultDhcpRelayConfig defaultConfig = (DefaultDhcpRelayConfig) config;
379 v4Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
380 v6Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
Yi Tseng483ac6f2017-08-02 15:03:31 -0700381 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700382 if (config instanceof IgnoreDhcpConfig) {
Yi Tseng525ff402017-10-23 19:39:39 -0700383 v4Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
384 v6Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
Yi Tseng51f1be92017-09-01 17:24:57 -0700385 }
386 }
387
388 protected void removeConfig(Config config) {
389 if (config instanceof IndirectDhcpRelayConfig) {
390 v4Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
391 v6Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
392 } else if (config instanceof DefaultDhcpRelayConfig) {
393 v4Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
394 v6Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
395 }
396 if (config instanceof IgnoreDhcpConfig) {
Yi Tseng525ff402017-10-23 19:39:39 -0700397 v4Handler.updateIgnoreVlanConfig(null);
398 v6Handler.updateIgnoreVlanConfig(null);
Yi Tseng51f1be92017-09-01 17:24:57 -0700399 }
400 }
401
Kalhee Kimba366062017-11-07 16:32:09 +0000402 private void processDhcpFpmRoutes(Boolean add) {
403 // needs to restore/remove fpm
404 }
405
406 public boolean isDhcpFpmEnabled() {
407 return dhcpFpmEnabled;
408 }
409
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700410 /**
411 * Request ARP packet in via PacketService.
412 */
413 private void requestArpPackets() {
Yi Tseng51f1be92017-09-01 17:24:57 -0700414 packetService.requestPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700415 }
416
417 /**
418 * Cancel requested ARP packets in via packet service.
419 */
420 private void cancelArpPackets() {
Yi Tseng51f1be92017-09-01 17:24:57 -0700421 packetService.cancelPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700422 }
423
424 @Override
425 public Optional<DhcpRecord> getDhcpRecord(HostId hostId) {
426 return dhcpRelayStore.getDhcpRecord(hostId);
427 }
428
429 @Override
430 public Collection<DhcpRecord> getDhcpRecords() {
431 return dhcpRelayStore.getDhcpRecords();
432 }
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000433 @Override
434 public void updateDhcpRecord(HostId hostId, DhcpRecord dhcpRecord) {
435 dhcpRelayStore.updateDhcpRecord(hostId, dhcpRecord);
436 }
Yi Tseng13a41a12017-07-26 13:45:01 -0700437 @Override
438 public Optional<MacAddress> getDhcpServerMacAddress() {
Yi Tseng4f2a0462017-08-31 11:21:00 -0700439 // TODO: depreated it
440 DefaultDhcpRelayConfig config = cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
441 DhcpServerConfig serverConfig = config.dhcpServerConfigs().get(0);
442 Ip4Address serverip = serverConfig.getDhcpServerIp4().get();
443 return hostService.getHostsByIp(serverip)
444 .stream()
445 .map(Host::mac)
446 .findFirst();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700447 }
448
Yi Tseng919b2df2017-09-07 16:22:51 -0700449 @Override
450 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
451 return ImmutableList.<DhcpServerInfo>builder()
452 .addAll(v4Handler.getDefaultDhcpServerInfoList())
453 .addAll(v6Handler.getDefaultDhcpServerInfoList())
454 .build();
455 }
456
457 @Override
458 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
459 return ImmutableList.<DhcpServerInfo>builder()
460 .addAll(v4Handler.getIndirectDhcpServerInfoList())
461 .addAll(v6Handler.getIndirectDhcpServerInfoList())
462 .build();
463 }
464
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700465 /**
466 * Gets DHCP data from a packet.
467 *
468 * @param packet the packet
469 * @return the DHCP data; empty if it is not a DHCP packet
470 */
471 private Optional<DHCP> findDhcp(Ethernet packet) {
472 return Stream.of(packet)
473 .filter(Objects::nonNull)
474 .map(Ethernet::getPayload)
475 .filter(p -> p instanceof IPv4)
476 .map(IPacket::getPayload)
477 .filter(Objects::nonNull)
478 .filter(p -> p instanceof UDP)
479 .map(IPacket::getPayload)
480 .filter(Objects::nonNull)
481 .filter(p -> p instanceof DHCP)
482 .map(p -> (DHCP) p)
483 .findFirst();
484 }
485
486 /**
487 * Gets DHCPv6 data from a packet.
488 *
489 * @param packet the packet
490 * @return the DHCPv6 data; empty if it is not a DHCPv6 packet
491 */
492 private Optional<DHCP6> findDhcp6(Ethernet packet) {
493 return Stream.of(packet)
494 .filter(Objects::nonNull)
495 .map(Ethernet::getPayload)
496 .filter(p -> p instanceof IPv6)
497 .map(IPacket::getPayload)
498 .filter(Objects::nonNull)
499 .filter(p -> p instanceof UDP)
500 .map(IPacket::getPayload)
501 .filter(Objects::nonNull)
502 .filter(p -> p instanceof DHCP6)
503 .map(p -> (DHCP6) p)
504 .findFirst();
505 }
506
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700507
508 private class DhcpRelayPacketProcessor implements PacketProcessor {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700509 @Override
510 public void process(PacketContext context) {
Charles Chan64a1c8e2019-01-23 15:03:17 -0800511 packetExecutor.execute(() -> processInternal(context));
512 }
513
514 private void processInternal(PacketContext context) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700515 // process the packet and get the payload
516 Ethernet packet = context.inPacket().parsed();
517 if (packet == null) {
518 return;
519 }
520
521 findDhcp(packet).ifPresent(dhcpPayload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700522 v4Handler.processDhcpPacket(context, dhcpPayload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700523 });
524
525 findDhcp6(packet).ifPresent(dhcp6Payload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700526 v6Handler.processDhcpPacket(context, dhcp6Payload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700527 });
528
529 if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
530 ARP arpPacket = (ARP) packet.getPayload();
531 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
532 Set<Interface> interfaces = interfaceService.
533 getInterfacesByPort(context.inPacket().receivedFrom());
534 //ignore the packets if dhcp server interface is not configured on onos.
535 if (interfaces.isEmpty()) {
536 log.warn("server virtual interface not configured");
537 return;
538 }
539 if ((arpPacket.getOpCode() != ARP.OP_REQUEST)) {
540 // handle request only
541 return;
542 }
543 MacAddress interfaceMac = interfaces.stream()
544 .filter(iface -> iface.vlan().equals(vlanId))
545 .map(Interface::mac)
546 .filter(mac -> !mac.equals(MacAddress.NONE))
547 .findFirst()
Yi Tseng51301292017-07-28 13:02:59 -0700548 .orElse(MacAddress.ONOS);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700549 if (interfaceMac == null) {
550 // can't find interface mac address
551 return;
552 }
553 processArpPacket(context, packet, interfaceMac);
554 }
555 }
556
557 /**
558 * Processes the ARP Payload and initiates a reply to the client.
559 *
560 * @param context the packet context
561 * @param packet the ethernet payload
562 * @param replyMac mac address to be replied
563 */
564 private void processArpPacket(PacketContext context, Ethernet packet, MacAddress replyMac) {
565 ARP arpPacket = (ARP) packet.getPayload();
Ray Milkeyf0c47612017-09-28 11:29:38 -0700566 ARP arpReply = arpPacket.duplicate();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700567 arpReply.setOpCode(ARP.OP_REPLY);
568
569 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
570 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
571 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
572 arpReply.setSenderHardwareAddress(replyMac.toBytes());
573
574 // Ethernet Frame.
575 Ethernet ethReply = new Ethernet();
576 ethReply.setSourceMACAddress(replyMac.toBytes());
577 ethReply.setDestinationMACAddress(packet.getSourceMAC());
578 ethReply.setEtherType(Ethernet.TYPE_ARP);
579 ethReply.setVlanID(packet.getVlanID());
580 ethReply.setPayload(arpReply);
581
582 ConnectPoint targetPort = context.inPacket().receivedFrom();
583 TrafficTreatment t = DefaultTrafficTreatment.builder()
584 .setOutput(targetPort.port()).build();
585 OutboundPacket o = new DefaultOutboundPacket(
586 targetPort.deviceId(), t, ByteBuffer.wrap(ethReply.serialize()));
587 if (log.isTraceEnabled()) {
588 log.trace("Relaying ARP packet {} to {}", packet, targetPort);
589 }
590 packetService.emit(o);
591 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700592 }
593
594 /**
595 * Listener for network config events.
596 */
597 private class InternalConfigListener implements NetworkConfigListener {
598 @Override
599 public void event(NetworkConfigEvent event) {
Yi Tseng51f1be92017-09-01 17:24:57 -0700600 switch (event.type()) {
601 case CONFIG_UPDATED:
602 case CONFIG_ADDED:
603 event.config().ifPresent(config -> {
604 updateConfig(config);
605 log.info("{} updated", config.getClass().getSimpleName());
606 });
607 break;
608 case CONFIG_REMOVED:
609 event.prevConfig().ifPresent(config -> {
610 removeConfig(config);
611 log.info("{} removed", config.getClass().getSimpleName());
612 });
613 break;
Charles Chan10b2fee2018-04-21 00:44:29 -0700614 case CONFIG_REGISTERED:
615 case CONFIG_UNREGISTERED:
616 break;
Yi Tseng51f1be92017-09-01 17:24:57 -0700617 default:
618 log.warn("Unsupported event type {}", event.type());
619 break;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700620 }
Charles Chan50443e82018-01-03 16:26:32 -0800621 }
Yi Tseng51f1be92017-09-01 17:24:57 -0700622
Charles Chan50443e82018-01-03 16:26:32 -0800623 @Override
624 public boolean isRelevant(NetworkConfigEvent event) {
625 if (event.configClass().equals(DefaultDhcpRelayConfig.class) ||
626 event.configClass().equals(IndirectDhcpRelayConfig.class) ||
627 event.configClass().equals(IgnoreDhcpConfig.class)) {
628 return true;
629 }
630 log.debug("Ignore irrelevant event class {}", event.configClass().getName());
631 return false;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700632 }
633 }
Yi Tseng127ffe52017-09-12 15:55:17 -0700634
635 private class InternalDeviceListener implements DeviceListener {
636
637 @Override
638 public void event(DeviceEvent event) {
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000639 if (devEventExecutor != null) {
Yi Tseng127ffe52017-09-12 15:55:17 -0700640 Device device = event.subject();
641 switch (event.type()) {
642 case DEVICE_ADDED:
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000643 devEventExecutor.execute(this::updateIgnoreVlanConfigs);
Yi Tseng127ffe52017-09-12 15:55:17 -0700644 break;
645 case DEVICE_AVAILABILITY_CHANGED:
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000646 devEventExecutor.execute(() -> deviceAvailabilityChanged(device));
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800647 break;
Yi Tseng127ffe52017-09-12 15:55:17 -0700648 default:
649 break;
650 }
Ruchi Sahotae4934e12019-03-01 16:56:07 +0000651 }
Yi Tseng127ffe52017-09-12 15:55:17 -0700652 }
653
654 private void deviceAvailabilityChanged(Device device) {
655 if (deviceService.isAvailable(device.id())) {
Yi Tseng525ff402017-10-23 19:39:39 -0700656 updateIgnoreVlanConfigs();
Saurav Dasb805f1a2017-12-13 16:19:35 -0800657 } else {
658 removeIgnoreVlanState();
Yi Tseng127ffe52017-09-12 15:55:17 -0700659 }
660 }
661
Yi Tseng525ff402017-10-23 19:39:39 -0700662 private void updateIgnoreVlanConfigs() {
Yi Tseng127ffe52017-09-12 15:55:17 -0700663 IgnoreDhcpConfig config = cfgService.getConfig(appId, IgnoreDhcpConfig.class);
Yi Tseng525ff402017-10-23 19:39:39 -0700664 v4Handler.updateIgnoreVlanConfig(config);
665 v6Handler.updateIgnoreVlanConfig(config);
Yi Tseng127ffe52017-09-12 15:55:17 -0700666 }
Saurav Dasb805f1a2017-12-13 16:19:35 -0800667
668 private void removeIgnoreVlanState() {
669 IgnoreDhcpConfig config = cfgService.getConfig(appId, IgnoreDhcpConfig.class);
670 v4Handler.removeIgnoreVlanState(config);
671 v6Handler.removeIgnoreVlanState(config);
672 }
Yi Tseng127ffe52017-09-12 15:55:17 -0700673 }
Kalhee Kimba366062017-11-07 16:32:09 +0000674
675
676
677 public Optional<FpmRecord> getFpmRecord(IpPrefix prefix) {
678 return dhcpFpmPrefixStore.getFpmRecord(prefix);
679 }
680
681 public Collection<FpmRecord> getFpmRecords() {
682 return dhcpFpmPrefixStore.getFpmRecords();
683 }
684
685 @Override
686 public void addFpmRecord(IpPrefix prefix, FpmRecord fpmRecord) {
687 dhcpFpmPrefixStore.addFpmRecord(prefix, fpmRecord);
688 }
689
690 @Override
691 public Optional<FpmRecord> removeFpmRecord(IpPrefix prefix) {
692 return dhcpFpmPrefixStore.removeFpmRecord(prefix);
693 }
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000694
695
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700696}