blob: 1ff500e40e717b4f00c754067f9823296a5fdfff [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;
26import java.util.stream.Stream;
27
Yi Tseng5479fb82017-09-01 17:24:57 -070028import com.google.common.collect.HashMultimap;
29import com.google.common.collect.ImmutableList;
30import com.google.common.collect.Multimap;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070031import org.apache.felix.scr.annotations.Activate;
32import org.apache.felix.scr.annotations.Component;
33import org.apache.felix.scr.annotations.Deactivate;
34import org.apache.felix.scr.annotations.Modified;
35import org.apache.felix.scr.annotations.Property;
36import org.apache.felix.scr.annotations.Reference;
37import org.apache.felix.scr.annotations.ReferenceCardinality;
38import org.apache.felix.scr.annotations.Service;
39import org.onlab.packet.ARP;
40import org.onlab.packet.DHCP;
41import org.onlab.packet.DHCP6;
42import org.onlab.packet.IPacket;
43import org.onlab.packet.IPv6;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070044import org.onlab.packet.Ethernet;
45import org.onlab.packet.IPv4;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070046import org.onlab.packet.MacAddress;
47import org.onlab.packet.TpPort;
48import org.onlab.packet.UDP;
49import org.onlab.packet.VlanId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070050import org.onlab.util.Tools;
51import org.onosproject.cfg.ComponentConfigService;
52import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070054import org.onosproject.dhcprelay.api.DhcpHandler;
55import org.onosproject.dhcprelay.api.DhcpRelayService;
Yi Tsenge72fbb52017-08-02 15:03:31 -070056import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
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 Tseng5479fb82017-09-01 17:24:57 -070061import org.onosproject.net.DeviceId;
Yi Tsenge72fbb52017-08-02 15:03:31 -070062import org.onosproject.net.config.Config;
Yi Tseng5479fb82017-09-01 17:24:57 -070063import org.onosproject.net.flow.criteria.Criterion;
64import org.onosproject.net.flow.criteria.UdpPortCriterion;
65import org.onosproject.net.flowobjective.DefaultForwardingObjective;
66import org.onosproject.net.flowobjective.FlowObjectiveService;
67import org.onosproject.net.flowobjective.ForwardingObjective;
68import org.onosproject.net.flowobjective.Objective;
69import org.onosproject.net.flowobjective.ObjectiveContext;
70import org.onosproject.net.flowobjective.ObjectiveError;
Ray Milkeyfacf2862017-08-03 11:58:29 -070071import org.onosproject.net.intf.Interface;
72import org.onosproject.net.intf.InterfaceService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070073import org.onosproject.net.ConnectPoint;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070074import org.onosproject.net.HostId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070075import org.onosproject.net.config.ConfigFactory;
76import org.onosproject.net.config.NetworkConfigEvent;
77import org.onosproject.net.config.NetworkConfigListener;
78import org.onosproject.net.config.NetworkConfigRegistry;
79import org.onosproject.net.flow.DefaultTrafficSelector;
80import org.onosproject.net.flow.DefaultTrafficTreatment;
81import org.onosproject.net.flow.TrafficSelector;
82import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070083import org.onosproject.net.host.HostService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070084import org.onosproject.net.packet.DefaultOutboundPacket;
85import org.onosproject.net.packet.OutboundPacket;
86import org.onosproject.net.packet.PacketContext;
87import org.onosproject.net.packet.PacketPriority;
88import org.onosproject.net.packet.PacketProcessor;
89import org.onosproject.net.packet.PacketService;
90import org.onosproject.net.provider.ProviderId;
91import org.osgi.service.component.ComponentContext;
92import org.slf4j.Logger;
93import org.slf4j.LoggerFactory;
94
95import com.google.common.collect.ImmutableSet;
96
Yi Tseng7a38f9a2017-06-09 14:36:40 -070097import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070098/**
99 * DHCP Relay Agent Application Component.
100 */
101@Component(immediate = true)
102@Service
103public class DhcpRelayManager implements DhcpRelayService {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700104 public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
Yi Tseng51301292017-07-28 13:02:59 -0700105 public static final ProviderId PROVIDER_ID = new ProviderId("host", DHCP_RELAY_APP);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700106 public static final String HOST_LOCATION_PROVIDER =
107 "org.onosproject.provider.host.impl.HostLocationProvider";
Yi Tseng325af8c2017-09-01 16:02:56 -0700108 public static final String ROUTE_STORE_IMPL =
109 "org.onosproject.routeservice.store.RouteStoreImpl";
Yi Tseng5479fb82017-09-01 17:24:57 -0700110 private static final TrafficSelector DHCP_SERVER_SELECTOR = DefaultTrafficSelector.builder()
111 .matchEthType(Ethernet.TYPE_IPV4)
112 .matchIPProtocol(IPv4.PROTOCOL_UDP)
113 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
114 .build();
115 private static final TrafficSelector DHCP_CLIENT_SELECTOR = DefaultTrafficSelector.builder()
116 .matchEthType(Ethernet.TYPE_IPV4)
117 .matchIPProtocol(IPv4.PROTOCOL_UDP)
118 .matchUdpDst(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
119 .build();
120 private static final TrafficSelector DHCP6_SERVER_SELECTOR = DefaultTrafficSelector.builder()
121 .matchEthType(Ethernet.TYPE_IPV6)
122 .matchIPProtocol(IPv4.PROTOCOL_UDP)
123 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
124 .build();
125 private static final TrafficSelector DHCP6_CLIENT_SELECTOR = DefaultTrafficSelector.builder()
126 .matchEthType(Ethernet.TYPE_IPV6)
127 .matchIPProtocol(IPv4.PROTOCOL_UDP)
128 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
129 .build();
130 static final List<TrafficSelector> DHCP_SELECTORS = ImmutableList.of(
131 DHCP_SERVER_SELECTOR,
132 DHCP_CLIENT_SELECTOR,
133 DHCP6_SERVER_SELECTOR,
134 DHCP6_CLIENT_SELECTOR
135 );
136 private static final TrafficSelector ARP_SELECTOR = DefaultTrafficSelector.builder()
137 .matchEthType(Ethernet.TYPE_ARP)
138 .build();
139 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700140 private final Logger log = LoggerFactory.getLogger(getClass());
141 private final InternalConfigListener cfgListener = new InternalConfigListener();
142
143 private final Set<ConfigFactory> factories = ImmutableSet.of(
Yi Tsenge72fbb52017-08-02 15:03:31 -0700144 new ConfigFactory<ApplicationId, DefaultDhcpRelayConfig>(APP_SUBJECT_FACTORY,
145 DefaultDhcpRelayConfig.class,
146 DefaultDhcpRelayConfig.KEY,
147 true) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700148 @Override
Yi Tsenge72fbb52017-08-02 15:03:31 -0700149 public DefaultDhcpRelayConfig createConfig() {
150 return new DefaultDhcpRelayConfig();
151 }
152 },
153 new ConfigFactory<ApplicationId, IndirectDhcpRelayConfig>(APP_SUBJECT_FACTORY,
154 IndirectDhcpRelayConfig.class,
155 IndirectDhcpRelayConfig.KEY,
156 true) {
157 @Override
158 public IndirectDhcpRelayConfig createConfig() {
159 return new IndirectDhcpRelayConfig();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700160 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700161 },
162 new ConfigFactory<ApplicationId, IgnoreDhcpConfig>(APP_SUBJECT_FACTORY,
163 IgnoreDhcpConfig.class,
164 IgnoreDhcpConfig.KEY,
165 true) {
166 @Override
167 public IgnoreDhcpConfig createConfig() {
168 return new IgnoreDhcpConfig();
169 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700170 }
171 );
172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
174 protected NetworkConfigRegistry cfgService;
175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
177 protected CoreService coreService;
178
179 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
180 protected PacketService packetService;
181
182 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
183 protected HostService hostService;
184
185 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700186 protected InterfaceService interfaceService;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
189 protected DhcpRelayStore dhcpRelayStore;
190
191 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
192 protected ComponentConfigService compCfgService;
193
Yi Tseng5479fb82017-09-01 17:24:57 -0700194 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
195 protected FlowObjectiveService flowObjectiveService;
196
Yi Tseng51301292017-07-28 13:02:59 -0700197 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
198 target = "(version=4)")
199 protected DhcpHandler v4Handler;
200
201 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
202 target = "(version=6)")
203 protected DhcpHandler v6Handler;
204
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700205 @Property(name = "arpEnabled", boolValue = true,
Yi Tseng51301292017-07-28 13:02:59 -0700206 label = "Enable Address resolution protocol")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700207 protected boolean arpEnabled = true;
208
Yi Tseng5479fb82017-09-01 17:24:57 -0700209 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700210 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700211 private ApplicationId appId;
212
213 @Activate
214 protected void activate(ComponentContext context) {
215 //start the dhcp relay agent
216 appId = coreService.registerApplication(DHCP_RELAY_APP);
217
218 cfgService.addListener(cfgListener);
219 factories.forEach(cfgService::registerConfigFactory);
220 //update the dhcp server configuration.
221 updateConfig();
Yi Tseng51301292017-07-28 13:02:59 -0700222
223 //add the packet processor
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700224 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Yi Tseng51301292017-07-28 13:02:59 -0700225
226 // listen host event for dhcp server or the gateway
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700227 requestDhcpPackets();
228 modified(context);
229
230 // disable dhcp from host location provider
231 compCfgService.preSetProperty(HOST_LOCATION_PROVIDER,
232 "useDhcp", Boolean.FALSE.toString());
Yi Tseng325af8c2017-09-01 16:02:56 -0700233 // Enable distribute route store
234 compCfgService.preSetProperty(ROUTE_STORE_IMPL,
235 "distributed", Boolean.TRUE.toString());
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700236 compCfgService.registerProperties(getClass());
237 log.info("DHCP-RELAY Started");
238 }
239
240 @Deactivate
241 protected void deactivate() {
242 cfgService.removeListener(cfgListener);
243 factories.forEach(cfgService::unregisterConfigFactory);
244 packetService.removeProcessor(dhcpRelayPacketProcessor);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700245 cancelDhcpPackets();
246 cancelArpPackets();
Yi Tseng51301292017-07-28 13:02:59 -0700247
Yi Tsengdfa8ee22017-08-07 12:45:01 -0700248 compCfgService.unregisterProperties(getClass(), false);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700249 log.info("DHCP-RELAY Stopped");
250 }
251
252 @Modified
253 protected void modified(ComponentContext context) {
254 Dictionary<?, ?> properties = context.getProperties();
255 Boolean flag;
256
257 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
258 if (flag != null) {
259 arpEnabled = flag;
260 log.info("Address resolution protocol is {}",
261 arpEnabled ? "enabled" : "disabled");
262 }
263
264 if (arpEnabled) {
265 requestArpPackets();
266 } else {
267 cancelArpPackets();
268 }
269 }
270
Yi Tsenge72fbb52017-08-02 15:03:31 -0700271 /**
272 * Updates DHCP relay app configuration.
273 */
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700274 private void updateConfig() {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700275 DefaultDhcpRelayConfig defaultConfig =
276 cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
277 IndirectDhcpRelayConfig indirectConfig =
278 cfgService.getConfig(appId, IndirectDhcpRelayConfig.class);
Yi Tseng5479fb82017-09-01 17:24:57 -0700279 IgnoreDhcpConfig ignoreDhcpConfig =
280 cfgService.getConfig(appId, IgnoreDhcpConfig.class);
Yi Tsenge72fbb52017-08-02 15:03:31 -0700281
282 if (defaultConfig != null) {
283 updateConfig(defaultConfig);
284 }
Yi Tsenge72fbb52017-08-02 15:03:31 -0700285 if (indirectConfig != null) {
286 updateConfig(indirectConfig);
287 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700288 if (ignoreDhcpConfig != null) {
289 updateConfig(ignoreDhcpConfig);
290 }
Yi Tsenge72fbb52017-08-02 15:03:31 -0700291 }
292
293 /**
294 * Updates DHCP relay app configuration with given configuration.
295 *
296 * @param config the configuration ot update
297 */
Yi Tseng5479fb82017-09-01 17:24:57 -0700298 protected void updateConfig(Config config) {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700299 if (config instanceof IndirectDhcpRelayConfig) {
300 IndirectDhcpRelayConfig indirectConfig = (IndirectDhcpRelayConfig) config;
301 v4Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
302 v6Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
Yi Tseng4fa05832017-08-17 13:08:31 -0700303 } else if (config instanceof DefaultDhcpRelayConfig) {
304 DefaultDhcpRelayConfig defaultConfig = (DefaultDhcpRelayConfig) config;
305 v4Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
306 v6Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
Yi Tsenge72fbb52017-08-02 15:03:31 -0700307 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700308 if (config instanceof IgnoreDhcpConfig) {
309 addIgnoreVlanRules((IgnoreDhcpConfig) config);
310 }
311 }
312
313 protected void removeConfig(Config config) {
314 if (config instanceof IndirectDhcpRelayConfig) {
315 v4Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
316 v6Handler.setIndirectDhcpServerConfigs(Collections.emptyList());
317 } else if (config instanceof DefaultDhcpRelayConfig) {
318 v4Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
319 v6Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
320 }
321 if (config instanceof IgnoreDhcpConfig) {
322 ignoredVlans.forEach(this::removeIgnoreVlanRule);
323 ignoredVlans.clear();
324 }
325 }
326
327 private void addIgnoreVlanRules(IgnoreDhcpConfig config) {
328 config.ignoredVlans().forEach((deviceId, vlanId) -> {
329 if (ignoredVlans.get(deviceId).contains(vlanId)) {
330 // don't need to process if it already ignored
331 return;
332 }
333 installIgnoreVlanRule(deviceId, vlanId);
334 ignoredVlans.put(deviceId, vlanId);
335 });
336
337 Multimap<DeviceId, VlanId> removedVlans = HashMultimap.create();
338 ignoredVlans.forEach((deviceId, vlanId) -> {
339 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
340 // not contains in new config, remove it
341 removeIgnoreVlanRule(deviceId, vlanId);
342 removedVlans.put(deviceId, vlanId);
343
344 }
345 });
346 removedVlans.forEach(ignoredVlans::remove);
347 }
348
349 private void installIgnoreVlanRule(DeviceId deviceId, VlanId vlanId) {
350 TrafficTreatment dropTreatment = DefaultTrafficTreatment.emptyTreatment();
351 dropTreatment.clearedDeferred();
352 DHCP_SELECTORS.forEach(trafficSelector -> {
353 UdpPortCriterion udpDst = (UdpPortCriterion) trafficSelector.getCriterion(Criterion.Type.UDP_DST);
354 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
355 .matchVlanId(vlanId)
356 .build();
357
358 ForwardingObjective fwd = DefaultForwardingObjective.builder()
359 .withFlag(ForwardingObjective.Flag.VERSATILE)
360 .withSelector(selector)
361 .withPriority(IGNORE_CONTROL_PRIORITY)
362 .withTreatment(dropTreatment)
363 .fromApp(appId)
364 .add(new ObjectiveContext() {
365 @Override
366 public void onSuccess(Objective objective) {
367 log.info("Vlan id {} from device {} ignored (UDP port {})",
368 vlanId, deviceId, udpDst.udpPort().toInt());
369 }
370
371 @Override
372 public void onError(Objective objective, ObjectiveError error) {
373 log.warn("Can't ignore vlan id {} from device {} due to {}",
374 vlanId, deviceId, error);
375 }
376 });
377 flowObjectiveService.apply(deviceId, fwd);
378 });
379 }
380
381 private void removeIgnoreVlanRule(DeviceId deviceId, VlanId vlanId) {
382 TrafficTreatment dropTreatment = DefaultTrafficTreatment.emptyTreatment();
383 dropTreatment.clearedDeferred();
384 DHCP_SELECTORS.forEach(trafficSelector -> {
385 UdpPortCriterion udpDst = (UdpPortCriterion) trafficSelector.getCriterion(Criterion.Type.UDP_DST);
386 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
387 .matchVlanId(vlanId)
388 .build();
389
390 ForwardingObjective fwd = DefaultForwardingObjective.builder()
391 .withFlag(ForwardingObjective.Flag.VERSATILE)
392 .withSelector(selector)
393 .withPriority(IGNORE_CONTROL_PRIORITY)
394 .withTreatment(dropTreatment)
395 .fromApp(appId)
396 .remove(new ObjectiveContext() {
397 @Override
398 public void onSuccess(Objective objective) {
399 log.info("Vlan id {} from device {} ignore rule removed (UDP port {})",
400 vlanId, deviceId, udpDst.udpPort().toInt());
401 }
402
403 @Override
404 public void onError(Objective objective, ObjectiveError error) {
405 log.warn("Can't remove ignore rule of vlan id {} from device {} due to {}",
406 vlanId, deviceId, error);
407 }
408 });
409 flowObjectiveService.apply(deviceId, fwd);
410 });
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700411 }
412
413 /**
414 * Request DHCP packet in via PacketService.
415 */
416 private void requestDhcpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700417 DHCP_SELECTORS.forEach(trafficSelector -> {
418 packetService.requestPackets(trafficSelector, PacketPriority.CONTROL, appId);
419 });
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700420 }
421
422 /**
423 * Cancel requested DHCP packets in via packet service.
424 */
425 private void cancelDhcpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700426 DHCP_SELECTORS.forEach(trafficSelector -> {
427 packetService.cancelPackets(trafficSelector, PacketPriority.CONTROL, appId);
428 });
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700429 }
430
431 /**
432 * Request ARP packet in via PacketService.
433 */
434 private void requestArpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700435 packetService.requestPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700436 }
437
438 /**
439 * Cancel requested ARP packets in via packet service.
440 */
441 private void cancelArpPackets() {
Yi Tseng5479fb82017-09-01 17:24:57 -0700442 packetService.cancelPackets(ARP_SELECTOR, PacketPriority.CONTROL, appId);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700443 }
444
445 @Override
446 public Optional<DhcpRecord> getDhcpRecord(HostId hostId) {
447 return dhcpRelayStore.getDhcpRecord(hostId);
448 }
449
450 @Override
451 public Collection<DhcpRecord> getDhcpRecords() {
452 return dhcpRelayStore.getDhcpRecords();
453 }
454
Yi Tseng13a41a12017-07-26 13:45:01 -0700455 @Override
456 public Optional<MacAddress> getDhcpServerMacAddress() {
Yi Tseng51301292017-07-28 13:02:59 -0700457 return v4Handler.getDhcpConnectMac();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700458 }
459
460 /**
461 * Gets DHCP data from a packet.
462 *
463 * @param packet the packet
464 * @return the DHCP data; empty if it is not a DHCP packet
465 */
466 private Optional<DHCP> findDhcp(Ethernet packet) {
467 return Stream.of(packet)
468 .filter(Objects::nonNull)
469 .map(Ethernet::getPayload)
470 .filter(p -> p instanceof IPv4)
471 .map(IPacket::getPayload)
472 .filter(Objects::nonNull)
473 .filter(p -> p instanceof UDP)
474 .map(IPacket::getPayload)
475 .filter(Objects::nonNull)
476 .filter(p -> p instanceof DHCP)
477 .map(p -> (DHCP) p)
478 .findFirst();
479 }
480
481 /**
482 * Gets DHCPv6 data from a packet.
483 *
484 * @param packet the packet
485 * @return the DHCPv6 data; empty if it is not a DHCPv6 packet
486 */
487 private Optional<DHCP6> findDhcp6(Ethernet packet) {
488 return Stream.of(packet)
489 .filter(Objects::nonNull)
490 .map(Ethernet::getPayload)
491 .filter(p -> p instanceof IPv6)
492 .map(IPacket::getPayload)
493 .filter(Objects::nonNull)
494 .filter(p -> p instanceof UDP)
495 .map(IPacket::getPayload)
496 .filter(Objects::nonNull)
497 .filter(p -> p instanceof DHCP6)
498 .map(p -> (DHCP6) p)
499 .findFirst();
500 }
501
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700502
503 private class DhcpRelayPacketProcessor implements PacketProcessor {
504
505 @Override
506 public void process(PacketContext context) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700507 // process the packet and get the payload
508 Ethernet packet = context.inPacket().parsed();
509 if (packet == null) {
510 return;
511 }
512
513 findDhcp(packet).ifPresent(dhcpPayload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700514 v4Handler.processDhcpPacket(context, dhcpPayload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700515 });
516
517 findDhcp6(packet).ifPresent(dhcp6Payload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700518 v6Handler.processDhcpPacket(context, dhcp6Payload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700519 });
520
521 if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
522 ARP arpPacket = (ARP) packet.getPayload();
523 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
524 Set<Interface> interfaces = interfaceService.
525 getInterfacesByPort(context.inPacket().receivedFrom());
526 //ignore the packets if dhcp server interface is not configured on onos.
527 if (interfaces.isEmpty()) {
528 log.warn("server virtual interface not configured");
529 return;
530 }
531 if ((arpPacket.getOpCode() != ARP.OP_REQUEST)) {
532 // handle request only
533 return;
534 }
535 MacAddress interfaceMac = interfaces.stream()
536 .filter(iface -> iface.vlan().equals(vlanId))
537 .map(Interface::mac)
538 .filter(mac -> !mac.equals(MacAddress.NONE))
539 .findFirst()
Yi Tseng51301292017-07-28 13:02:59 -0700540 .orElse(MacAddress.ONOS);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700541 if (interfaceMac == null) {
542 // can't find interface mac address
543 return;
544 }
545 processArpPacket(context, packet, interfaceMac);
546 }
547 }
548
549 /**
550 * Processes the ARP Payload and initiates a reply to the client.
551 *
552 * @param context the packet context
553 * @param packet the ethernet payload
554 * @param replyMac mac address to be replied
555 */
556 private void processArpPacket(PacketContext context, Ethernet packet, MacAddress replyMac) {
557 ARP arpPacket = (ARP) packet.getPayload();
558 ARP arpReply = (ARP) arpPacket.clone();
559 arpReply.setOpCode(ARP.OP_REPLY);
560
561 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
562 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
563 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
564 arpReply.setSenderHardwareAddress(replyMac.toBytes());
565
566 // Ethernet Frame.
567 Ethernet ethReply = new Ethernet();
568 ethReply.setSourceMACAddress(replyMac.toBytes());
569 ethReply.setDestinationMACAddress(packet.getSourceMAC());
570 ethReply.setEtherType(Ethernet.TYPE_ARP);
571 ethReply.setVlanID(packet.getVlanID());
572 ethReply.setPayload(arpReply);
573
574 ConnectPoint targetPort = context.inPacket().receivedFrom();
575 TrafficTreatment t = DefaultTrafficTreatment.builder()
576 .setOutput(targetPort.port()).build();
577 OutboundPacket o = new DefaultOutboundPacket(
578 targetPort.deviceId(), t, ByteBuffer.wrap(ethReply.serialize()));
579 if (log.isTraceEnabled()) {
580 log.trace("Relaying ARP packet {} to {}", packet, targetPort);
581 }
582 packetService.emit(o);
583 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700584 }
585
586 /**
587 * Listener for network config events.
588 */
589 private class InternalConfigListener implements NetworkConfigListener {
590 @Override
591 public void event(NetworkConfigEvent event) {
Yi Tseng5479fb82017-09-01 17:24:57 -0700592 switch (event.type()) {
593 case CONFIG_UPDATED:
594 case CONFIG_ADDED:
595 event.config().ifPresent(config -> {
596 updateConfig(config);
597 log.info("{} updated", config.getClass().getSimpleName());
598 });
599 break;
600 case CONFIG_REMOVED:
601 event.prevConfig().ifPresent(config -> {
602 removeConfig(config);
603 log.info("{} removed", config.getClass().getSimpleName());
604 });
605 break;
606 default:
607 log.warn("Unsupported event type {}", event.type());
608 break;
Yi Tsenge72fbb52017-08-02 15:03:31 -0700609 }
Yi Tseng5479fb82017-09-01 17:24:57 -0700610
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700611 }
612 }
613}