blob: 2d7663912866630858011a3330cabb555cdaafa1 [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 Tseng5479fb82017-09-01 17:24:57 -070020import java.util.Collections;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070021import java.util.Dictionary;
Yi Tseng5479fb82017-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 Tseng76365d82017-09-12 15:55:17 -070026import java.util.concurrent.atomic.AtomicInteger;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070027import java.util.stream.Stream;
28
Yi Tseng7a38f9a2017-06-09 14:36:40 -070029import org.apache.felix.scr.annotations.Activate;
30import org.apache.felix.scr.annotations.Component;
31import org.apache.felix.scr.annotations.Deactivate;
32import org.apache.felix.scr.annotations.Modified;
33import org.apache.felix.scr.annotations.Property;
34import org.apache.felix.scr.annotations.Reference;
35import org.apache.felix.scr.annotations.ReferenceCardinality;
36import org.apache.felix.scr.annotations.Service;
37import org.onlab.packet.ARP;
38import org.onlab.packet.DHCP;
39import org.onlab.packet.DHCP6;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070040import org.onlab.packet.Ethernet;
Yi Tseng4ec727d2017-08-31 11:21:00 -070041import org.onlab.packet.IPacket;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070042import org.onlab.packet.IPv4;
Yi Tseng4ec727d2017-08-31 11:21:00 -070043import org.onlab.packet.IPv6;
44import org.onlab.packet.Ip4Address;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070045import org.onlab.packet.MacAddress;
46import org.onlab.packet.TpPort;
47import org.onlab.packet.UDP;
48import org.onlab.packet.VlanId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070049import org.onlab.util.Tools;
50import org.onosproject.cfg.ComponentConfigService;
51import org.onosproject.core.ApplicationId;
52import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070053import org.onosproject.dhcprelay.api.DhcpHandler;
54import org.onosproject.dhcprelay.api.DhcpRelayService;
Yi Tsenge72fbb52017-08-02 15:03:31 -070055import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
Yi Tseng4ec727d2017-08-31 11:21:00 -070056import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng5479fb82017-09-01 17:24:57 -070057import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Yi Tsenge72fbb52017-08-02 15:03:31 -070058import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070059import org.onosproject.dhcprelay.store.DhcpRecord;
60import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070061import org.onosproject.net.ConnectPoint;
Yi Tseng76365d82017-09-12 15:55:17 -070062import org.onosproject.net.Device;
Yi Tseng4ec727d2017-08-31 11:21:00 -070063import org.onosproject.net.DeviceId;
64import org.onosproject.net.Host;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070065import org.onosproject.net.HostId;
Yi Tseng4ec727d2017-08-31 11:21:00 -070066import org.onosproject.net.config.Config;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070067import org.onosproject.net.config.ConfigFactory;
68import org.onosproject.net.config.NetworkConfigEvent;
69import org.onosproject.net.config.NetworkConfigListener;
70import org.onosproject.net.config.NetworkConfigRegistry;
71import org.onosproject.net.flow.DefaultTrafficSelector;
72import org.onosproject.net.flow.DefaultTrafficTreatment;
73import org.onosproject.net.flow.TrafficSelector;
74import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng76365d82017-09-12 15:55:17 -070075import org.onosproject.net.behaviour.Pipeliner;
76import org.onosproject.net.device.DeviceEvent;
77import org.onosproject.net.device.DeviceListener;
78import org.onosproject.net.device.DeviceService;
Yi Tseng4ec727d2017-08-31 11:21:00 -070079import org.onosproject.net.flow.criteria.Criterion;
80import org.onosproject.net.flow.criteria.UdpPortCriterion;
81import org.onosproject.net.flowobjective.DefaultForwardingObjective;
82import org.onosproject.net.flowobjective.FlowObjectiveService;
83import org.onosproject.net.flowobjective.ForwardingObjective;
84import org.onosproject.net.flowobjective.Objective;
85import org.onosproject.net.flowobjective.ObjectiveContext;
86import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070087import org.onosproject.net.host.HostService;
Yi Tseng4ec727d2017-08-31 11:21:00 -070088import org.onosproject.net.intf.Interface;
89import org.onosproject.net.intf.InterfaceService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070090import org.onosproject.net.packet.DefaultOutboundPacket;
91import org.onosproject.net.packet.OutboundPacket;
92import org.onosproject.net.packet.PacketContext;
93import org.onosproject.net.packet.PacketPriority;
94import org.onosproject.net.packet.PacketProcessor;
95import org.onosproject.net.packet.PacketService;
96import org.onosproject.net.provider.ProviderId;
97import org.osgi.service.component.ComponentContext;
98import org.slf4j.Logger;
99import org.slf4j.LoggerFactory;
100
Yi Tseng4ec727d2017-08-31 11:21:00 -0700101import com.google.common.collect.HashMultimap;
102import com.google.common.collect.ImmutableList;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700103import com.google.common.collect.ImmutableSet;
Yi Tseng4ec727d2017-08-31 11:21:00 -0700104import com.google.common.collect.Multimap;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700105
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700106import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
Yi Tseng76365d82017-09-12 15:55:17 -0700107import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
108import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
109
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700110/**
111 * DHCP Relay Agent Application Component.
112 */
113@Component(immediate = true)
114@Service
115public class DhcpRelayManager implements DhcpRelayService {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700116 public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
Yi Tseng51301292017-07-28 13:02:59 -0700117 public static final ProviderId PROVIDER_ID = new ProviderId("host", DHCP_RELAY_APP);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700118 public static final String HOST_LOCATION_PROVIDER =
119 "org.onosproject.provider.host.impl.HostLocationProvider";
Yi Tseng325af8c2017-09-01 16:02:56 -0700120 public static final String ROUTE_STORE_IMPL =
121 "org.onosproject.routeservice.store.RouteStoreImpl";
Yi Tseng5479fb82017-09-01 17:24:57 -0700122 private static final TrafficSelector DHCP_SERVER_SELECTOR = DefaultTrafficSelector.builder()
123 .matchEthType(Ethernet.TYPE_IPV4)
124 .matchIPProtocol(IPv4.PROTOCOL_UDP)
125 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
126 .build();
127 private static final TrafficSelector DHCP_CLIENT_SELECTOR = DefaultTrafficSelector.builder()
128 .matchEthType(Ethernet.TYPE_IPV4)
129 .matchIPProtocol(IPv4.PROTOCOL_UDP)
130 .matchUdpDst(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
131 .build();
132 private static final TrafficSelector DHCP6_SERVER_SELECTOR = DefaultTrafficSelector.builder()
133 .matchEthType(Ethernet.TYPE_IPV6)
134 .matchIPProtocol(IPv4.PROTOCOL_UDP)
135 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
136 .build();
137 private static final TrafficSelector DHCP6_CLIENT_SELECTOR = DefaultTrafficSelector.builder()
138 .matchEthType(Ethernet.TYPE_IPV6)
139 .matchIPProtocol(IPv4.PROTOCOL_UDP)
140 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
141 .build();
142 static final List<TrafficSelector> DHCP_SELECTORS = ImmutableList.of(
143 DHCP_SERVER_SELECTOR,
144 DHCP_CLIENT_SELECTOR,
145 DHCP6_SERVER_SELECTOR,
146 DHCP6_CLIENT_SELECTOR
147 );
148 private static final TrafficSelector ARP_SELECTOR = DefaultTrafficSelector.builder()
149 .matchEthType(Ethernet.TYPE_ARP)
150 .build();
151 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700152 private final Logger log = LoggerFactory.getLogger(getClass());
153 private final InternalConfigListener cfgListener = new InternalConfigListener();
154
155 private final Set<ConfigFactory> factories = ImmutableSet.of(
Yi Tsenge72fbb52017-08-02 15:03:31 -0700156 new ConfigFactory<ApplicationId, DefaultDhcpRelayConfig>(APP_SUBJECT_FACTORY,
157 DefaultDhcpRelayConfig.class,
158 DefaultDhcpRelayConfig.KEY,
159 true) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700160 @Override
Yi Tsenge72fbb52017-08-02 15:03:31 -0700161 public DefaultDhcpRelayConfig createConfig() {
162 return new DefaultDhcpRelayConfig();
163 }
164 },
165 new ConfigFactory<ApplicationId, IndirectDhcpRelayConfig>(APP_SUBJECT_FACTORY,
166 IndirectDhcpRelayConfig.class,
167 IndirectDhcpRelayConfig.KEY,
168 true) {
169 @Override
170 public IndirectDhcpRelayConfig createConfig() {
171 return new IndirectDhcpRelayConfig();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700172 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700173 },
174 new ConfigFactory<ApplicationId, IgnoreDhcpConfig>(APP_SUBJECT_FACTORY,
175 IgnoreDhcpConfig.class,
176 IgnoreDhcpConfig.KEY,
177 true) {
178 @Override
179 public IgnoreDhcpConfig createConfig() {
180 return new IgnoreDhcpConfig();
181 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700182 }
183 );
184
185 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
186 protected NetworkConfigRegistry cfgService;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
189 protected CoreService coreService;
190
191 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
192 protected PacketService packetService;
193
194 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
195 protected HostService hostService;
196
197 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700198 protected InterfaceService interfaceService;
199
200 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
201 protected DhcpRelayStore dhcpRelayStore;
202
203 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
204 protected ComponentConfigService compCfgService;
205
Yi Tseng5479fb82017-09-01 17:24:57 -0700206 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
207 protected FlowObjectiveService flowObjectiveService;
208
Yi Tseng76365d82017-09-12 15:55:17 -0700209 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
210 protected DeviceService deviceService;
211
Yi Tseng51301292017-07-28 13:02:59 -0700212 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
Yi Tseng76365d82017-09-12 15:55:17 -0700213 target = "(version=4)")
Yi Tseng51301292017-07-28 13:02:59 -0700214 protected DhcpHandler v4Handler;
215
216 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
Yi Tseng76365d82017-09-12 15:55:17 -0700217 target = "(version=6)")
Yi Tseng51301292017-07-28 13:02:59 -0700218 protected DhcpHandler v6Handler;
219
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700220 @Property(name = "arpEnabled", boolValue = true,
Yi Tseng76365d82017-09-12 15:55:17 -0700221 label = "Enable Address resolution protocol")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700222 protected boolean arpEnabled = true;
223
Yi Tseng5479fb82017-09-01 17:24:57 -0700224 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
Yi Tseng76365d82017-09-12 15:55:17 -0700225 protected DeviceListener deviceListener = new InternalDeviceListener();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700226 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700227 private ApplicationId appId;
228
229 @Activate
230 protected void activate(ComponentContext context) {
231 //start the dhcp relay agent
232 appId = coreService.registerApplication(DHCP_RELAY_APP);
233
234 cfgService.addListener(cfgListener);
235 factories.forEach(cfgService::registerConfigFactory);
236 //update the dhcp server configuration.
237 updateConfig();
Yi Tseng51301292017-07-28 13:02:59 -0700238
239 //add the packet processor
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700240 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Yi Tseng51301292017-07-28 13:02:59 -0700241
242 // listen host event for dhcp server or the gateway
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700243 requestDhcpPackets();
244 modified(context);
245
246 // disable dhcp from host location provider
247 compCfgService.preSetProperty(HOST_LOCATION_PROVIDER,
248 "useDhcp", Boolean.FALSE.toString());
Yi Tseng325af8c2017-09-01 16:02:56 -0700249 // Enable distribute route store
250 compCfgService.preSetProperty(ROUTE_STORE_IMPL,
251 "distributed", Boolean.TRUE.toString());
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700252 compCfgService.registerProperties(getClass());
Yi Tseng76365d82017-09-12 15:55:17 -0700253
254 deviceService.addListener(deviceListener);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700255 log.info("DHCP-RELAY Started");
256 }
257
258 @Deactivate
259 protected void deactivate() {
260 cfgService.removeListener(cfgListener);
261 factories.forEach(cfgService::unregisterConfigFactory);
262 packetService.removeProcessor(dhcpRelayPacketProcessor);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700263 cancelDhcpPackets();
264 cancelArpPackets();
Yi Tsengdfa8ee22017-08-07 12:45:01 -0700265 compCfgService.unregisterProperties(getClass(), false);
Yi Tseng76365d82017-09-12 15:55:17 -0700266 deviceService.removeListener(deviceListener);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700267 log.info("DHCP-RELAY Stopped");
268 }
269
270 @Modified
271 protected void modified(ComponentContext context) {
272 Dictionary<?, ?> properties = context.getProperties();
273 Boolean flag;
274
275 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
276 if (flag != null) {
277 arpEnabled = flag;
278 log.info("Address resolution protocol is {}",
279 arpEnabled ? "enabled" : "disabled");
280 }
281
282 if (arpEnabled) {
283 requestArpPackets();
284 } else {
285 cancelArpPackets();
286 }
287 }
288
Yi Tsenge72fbb52017-08-02 15:03:31 -0700289 /**
290 * Updates DHCP relay app configuration.
291 */
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700292 private void updateConfig() {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700293 DefaultDhcpRelayConfig defaultConfig =
294 cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
295 IndirectDhcpRelayConfig indirectConfig =
296 cfgService.getConfig(appId, IndirectDhcpRelayConfig.class);
Yi Tseng5479fb82017-09-01 17:24:57 -0700297 IgnoreDhcpConfig ignoreDhcpConfig =
298 cfgService.getConfig(appId, IgnoreDhcpConfig.class);
Yi Tsenge72fbb52017-08-02 15:03:31 -0700299
300 if (defaultConfig != null) {
301 updateConfig(defaultConfig);
302 }
Yi Tsenge72fbb52017-08-02 15:03:31 -0700303 if (indirectConfig != null) {
304 updateConfig(indirectConfig);
305 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700306 if (ignoreDhcpConfig != null) {
307 updateConfig(ignoreDhcpConfig);
308 }
Yi Tsenge72fbb52017-08-02 15:03:31 -0700309 }
310
311 /**
312 * Updates DHCP relay app configuration with given configuration.
313 *
314 * @param config the configuration ot update
315 */
Yi Tseng5479fb82017-09-01 17:24:57 -0700316 protected void updateConfig(Config config) {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700317 if (config instanceof IndirectDhcpRelayConfig) {
318 IndirectDhcpRelayConfig indirectConfig = (IndirectDhcpRelayConfig) config;
319 v4Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
320 v6Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
Yi Tseng4fa05832017-08-17 13:08:31 -0700321 } else if (config instanceof DefaultDhcpRelayConfig) {
322 DefaultDhcpRelayConfig defaultConfig = (DefaultDhcpRelayConfig) config;
323 v4Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
324 v6Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
Yi Tsenge72fbb52017-08-02 15:03:31 -0700325 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700326 if (config instanceof IgnoreDhcpConfig) {
Yi Tseng76365d82017-09-12 15:55:17 -0700327 updateIgnoreVlanRules((IgnoreDhcpConfig) config);
Yi Tseng5479fb82017-09-01 17:24:57 -0700328 }
329 }
330
331 protected void removeConfig(Config config) {
332 if (config instanceof IndirectDhcpRelayConfig) {
333 v4Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
334 v6Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
335 } else if (config instanceof DefaultDhcpRelayConfig) {
336 v4Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
337 v6Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
338 }
339 if (config instanceof IgnoreDhcpConfig) {
Yi Tseng76365d82017-09-12 15:55:17 -0700340 ignoredVlans.forEach(((deviceId, vlanId) -> {
341 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
342 }));
Yi Tseng5479fb82017-09-01 17:24:57 -0700343 }
344 }
345
Yi Tseng76365d82017-09-12 15:55:17 -0700346 private void updateIgnoreVlanRules(IgnoreDhcpConfig config) {
Yi Tseng5479fb82017-09-01 17:24:57 -0700347 config.ignoredVlans().forEach((deviceId, vlanId) -> {
348 if (ignoredVlans.get(deviceId).contains(vlanId)) {
349 // don't need to process if it already ignored
350 return;
351 }
Yi Tseng76365d82017-09-12 15:55:17 -0700352 processIgnoreVlanRule(deviceId, vlanId, ADD);
Yi Tseng5479fb82017-09-01 17:24:57 -0700353 });
354
Yi Tseng5479fb82017-09-01 17:24:57 -0700355 ignoredVlans.forEach((deviceId, vlanId) -> {
356 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
357 // not contains in new config, remove it
Yi Tseng76365d82017-09-12 15:55:17 -0700358 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
Yi Tseng5479fb82017-09-01 17:24:57 -0700359 }
360 });
Yi Tseng5479fb82017-09-01 17:24:57 -0700361 }
362
Yi Tseng76365d82017-09-12 15:55:17 -0700363 /**
364 * Process the ignore rules.
365 *
366 * @param deviceId the device id
367 * @param vlanId the vlan to be ignored
368 * @param op the operation, ADD to install; REMOVE to uninstall rules
369 */
370 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng5479fb82017-09-01 17:24:57 -0700371 TrafficTreatment dropTreatment = DefaultTrafficTreatment.emptyTreatment();
372 dropTreatment.clearedDeferred();
Yi Tseng76365d82017-09-12 15:55:17 -0700373 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
Yi Tseng5479fb82017-09-01 17:24:57 -0700374 DHCP_SELECTORS.forEach(trafficSelector -> {
375 UdpPortCriterion udpDst = (UdpPortCriterion) trafficSelector.getCriterion(Criterion.Type.UDP_DST);
Yi Tseng76365d82017-09-12 15:55:17 -0700376 int udpDstPort = udpDst.udpPort().toInt();
Yi Tseng5479fb82017-09-01 17:24:57 -0700377 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
378 .matchVlanId(vlanId)
379 .build();
380
Yi Tseng76365d82017-09-12 15:55:17 -0700381 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
Yi Tseng5479fb82017-09-01 17:24:57 -0700382 .withFlag(ForwardingObjective.Flag.VERSATILE)
383 .withSelector(selector)
384 .withPriority(IGNORE_CONTROL_PRIORITY)
385 .withTreatment(dropTreatment)
Yi Tseng76365d82017-09-12 15:55:17 -0700386 .fromApp(appId);
Yi Tseng5479fb82017-09-01 17:24:57 -0700387
Yi Tseng5479fb82017-09-01 17:24:57 -0700388
Yi Tseng76365d82017-09-12 15:55:17 -0700389 ObjectiveContext objectiveContext = new ObjectiveContext() {
390 @Override
391 public void onSuccess(Objective objective) {
392 log.info("Ignore rule {} (Vlan id {}, device {}, UDP dst {})",
393 op, vlanId, deviceId, udpDstPort);
394 int countDown = installedCount.decrementAndGet();
395 if (countDown != 0) {
396 return;
397 }
398 switch (op) {
399 case ADD:
Yi Tseng5479fb82017-09-01 17:24:57 -0700400
Yi Tseng76365d82017-09-12 15:55:17 -0700401 ignoredVlans.put(deviceId, vlanId);
402 break;
403 case REMOVE:
404 ignoredVlans.remove(deviceId, vlanId);
405 break;
406 default:
407 log.warn("Unsupported objective operation {}", op);
408 break;
409 }
410 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700411
Yi Tseng76365d82017-09-12 15:55:17 -0700412 @Override
413 public void onError(Objective objective, ObjectiveError error) {
414 log.warn("Can't {} ignore rule (vlan id {}, udp dst {}, device {}) due to {}",
415 op, vlanId, udpDstPort, deviceId, error);
416 }
417 };
418
419 ForwardingObjective fwd;
420 switch (op) {
421 case ADD:
422 fwd = builder.add(objectiveContext);
423 break;
424 case REMOVE:
425 fwd = builder.remove(objectiveContext);
426 break;
427 default:
428 log.warn("Unsupported objective operation {}", op);
429 return;
430 }
431
432 Device device = deviceService.getDevice(deviceId);
433 if (device == null || !device.is(Pipeliner.class)) {
434 log.warn("Device {} is not available now, wait until device is available", deviceId);
435 return;
436 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700437 flowObjectiveService.apply(deviceId, fwd);
438 });
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700439 }
440
441 /**
442 * Request DHCP packet in via PacketService.
443 */
444 private void requestDhcpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700445 DHCP_SELECTORS.forEach(trafficSelector -> {
446 packetService.requestPackets(trafficSelector, PacketPriority.CONTROL, appId);
447 });
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700448 }
449
450 /**
451 * Cancel requested DHCP packets in via packet service.
452 */
453 private void cancelDhcpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700454 DHCP_SELECTORS.forEach(trafficSelector -> {
455 packetService.cancelPackets(trafficSelector, PacketPriority.CONTROL, appId);
456 });
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700457 }
458
459 /**
460 * Request ARP packet in via PacketService.
461 */
462 private void requestArpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700463 packetService.requestPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700464 }
465
466 /**
467 * Cancel requested ARP packets in via packet service.
468 */
469 private void cancelArpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700470 packetService.cancelPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700471 }
472
473 @Override
474 public Optional<DhcpRecord> getDhcpRecord(HostId hostId) {
475 return dhcpRelayStore.getDhcpRecord(hostId);
476 }
477
478 @Override
479 public Collection<DhcpRecord> getDhcpRecords() {
480 return dhcpRelayStore.getDhcpRecords();
481 }
482
Yi Tseng13a41a12017-07-26 13:45:01 -0700483 @Override
484 public Optional<MacAddress> getDhcpServerMacAddress() {
Yi Tseng4ec727d2017-08-31 11:21:00 -0700485 // TODO: depreated it
486 DefaultDhcpRelayConfig config = cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
487 DhcpServerConfig serverConfig = config.dhcpServerConfigs().get(0);
488 Ip4Address serverip = serverConfig.getDhcpServerIp4().get();
489 return hostService.getHostsByIp(serverip)
490 .stream()
491 .map(Host::mac)
492 .findFirst();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700493 }
494
495 /**
496 * Gets DHCP data from a packet.
497 *
498 * @param packet the packet
499 * @return the DHCP data; empty if it is not a DHCP packet
500 */
501 private Optional<DHCP> findDhcp(Ethernet packet) {
502 return Stream.of(packet)
503 .filter(Objects::nonNull)
504 .map(Ethernet::getPayload)
505 .filter(p -> p instanceof IPv4)
506 .map(IPacket::getPayload)
507 .filter(Objects::nonNull)
508 .filter(p -> p instanceof UDP)
509 .map(IPacket::getPayload)
510 .filter(Objects::nonNull)
511 .filter(p -> p instanceof DHCP)
512 .map(p -> (DHCP) p)
513 .findFirst();
514 }
515
516 /**
517 * Gets DHCPv6 data from a packet.
518 *
519 * @param packet the packet
520 * @return the DHCPv6 data; empty if it is not a DHCPv6 packet
521 */
522 private Optional<DHCP6> findDhcp6(Ethernet packet) {
523 return Stream.of(packet)
524 .filter(Objects::nonNull)
525 .map(Ethernet::getPayload)
526 .filter(p -> p instanceof IPv6)
527 .map(IPacket::getPayload)
528 .filter(Objects::nonNull)
529 .filter(p -> p instanceof UDP)
530 .map(IPacket::getPayload)
531 .filter(Objects::nonNull)
532 .filter(p -> p instanceof DHCP6)
533 .map(p -> (DHCP6) p)
534 .findFirst();
535 }
536
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700537
538 private class DhcpRelayPacketProcessor implements PacketProcessor {
539
540 @Override
541 public void process(PacketContext context) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700542 // process the packet and get the payload
543 Ethernet packet = context.inPacket().parsed();
544 if (packet == null) {
545 return;
546 }
547
548 findDhcp(packet).ifPresent(dhcpPayload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700549 v4Handler.processDhcpPacket(context, dhcpPayload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700550 });
551
552 findDhcp6(packet).ifPresent(dhcp6Payload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700553 v6Handler.processDhcpPacket(context, dhcp6Payload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700554 });
555
556 if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
557 ARP arpPacket = (ARP) packet.getPayload();
558 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
559 Set<Interface> interfaces = interfaceService.
560 getInterfacesByPort(context.inPacket().receivedFrom());
561 //ignore the packets if dhcp server interface is not configured on onos.
562 if (interfaces.isEmpty()) {
563 log.warn("server virtual interface not configured");
564 return;
565 }
566 if ((arpPacket.getOpCode() != ARP.OP_REQUEST)) {
567 // handle request only
568 return;
569 }
570 MacAddress interfaceMac = interfaces.stream()
571 .filter(iface -> iface.vlan().equals(vlanId))
572 .map(Interface::mac)
573 .filter(mac -> !mac.equals(MacAddress.NONE))
574 .findFirst()
Yi Tseng51301292017-07-28 13:02:59 -0700575 .orElse(MacAddress.ONOS);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700576 if (interfaceMac == null) {
577 // can't find interface mac address
578 return;
579 }
580 processArpPacket(context, packet, interfaceMac);
581 }
582 }
583
584 /**
585 * Processes the ARP Payload and initiates a reply to the client.
586 *
587 * @param context the packet context
588 * @param packet the ethernet payload
589 * @param replyMac mac address to be replied
590 */
591 private void processArpPacket(PacketContext context, Ethernet packet, MacAddress replyMac) {
592 ARP arpPacket = (ARP) packet.getPayload();
593 ARP arpReply = (ARP) arpPacket.clone();
594 arpReply.setOpCode(ARP.OP_REPLY);
595
596 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
597 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
598 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
599 arpReply.setSenderHardwareAddress(replyMac.toBytes());
600
601 // Ethernet Frame.
602 Ethernet ethReply = new Ethernet();
603 ethReply.setSourceMACAddress(replyMac.toBytes());
604 ethReply.setDestinationMACAddress(packet.getSourceMAC());
605 ethReply.setEtherType(Ethernet.TYPE_ARP);
606 ethReply.setVlanID(packet.getVlanID());
607 ethReply.setPayload(arpReply);
608
609 ConnectPoint targetPort = context.inPacket().receivedFrom();
610 TrafficTreatment t = DefaultTrafficTreatment.builder()
611 .setOutput(targetPort.port()).build();
612 OutboundPacket o = new DefaultOutboundPacket(
613 targetPort.deviceId(), t, ByteBuffer.wrap(ethReply.serialize()));
614 if (log.isTraceEnabled()) {
615 log.trace("Relaying ARP packet {} to {}", packet, targetPort);
616 }
617 packetService.emit(o);
618 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700619 }
620
621 /**
622 * Listener for network config events.
623 */
624 private class InternalConfigListener implements NetworkConfigListener {
625 @Override
626 public void event(NetworkConfigEvent event) {
Yi Tseng5479fb82017-09-01 17:24:57 -0700627 switch (event.type()) {
628 case CONFIG_UPDATED:
629 case CONFIG_ADDED:
630 event.config().ifPresent(config -> {
631 updateConfig(config);
632 log.info("{} updated", config.getClass().getSimpleName());
633 });
634 break;
635 case CONFIG_REMOVED:
636 event.prevConfig().ifPresent(config -> {
637 removeConfig(config);
638 log.info("{} removed", config.getClass().getSimpleName());
639 });
640 break;
641 default:
642 log.warn("Unsupported event type {}", event.type());
643 break;
Yi Tsenge72fbb52017-08-02 15:03:31 -0700644 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700645
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700646 }
647 }
Yi Tseng76365d82017-09-12 15:55:17 -0700648
649 private class InternalDeviceListener implements DeviceListener {
650
651 @Override
652 public void event(DeviceEvent event) {
653 Device device = event.subject();
654 switch (event.type()) {
655 case DEVICE_ADDED:
656 deviceAdd(device.id());
657 break;
658 case DEVICE_REMOVED:
659 ignoredVlans.removeAll(device.id());
660 break;
661 case DEVICE_AVAILABILITY_CHANGED:
662 deviceAvailabilityChanged(device);
663
664 default:
665 break;
666 }
667 }
668
669 private void deviceAvailabilityChanged(Device device) {
670 if (deviceService.isAvailable(device.id())) {
671 deviceAdd(device.id());
672 } else {
673 ignoredVlans.removeAll(device.id());
674 }
675 }
676
677 private void deviceAdd(DeviceId deviceId) {
678 IgnoreDhcpConfig config = cfgService.getConfig(appId, IgnoreDhcpConfig.class);
679 Collection<VlanId> vlanIds = config.ignoredVlans().get(deviceId);
680 vlanIds.forEach(vlanId -> {
681 processIgnoreVlanRule(deviceId, vlanId, ADD);
682 });
683 }
684 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700685}