blob: 99b8afbef0c4629e3a6442719f454a4fd6b77b8c [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 Tseng7a38f9a2017-06-09 14:36:40 -070020import java.util.Dictionary;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070021import java.util.Objects;
22import java.util.Optional;
23import java.util.Set;
24import java.util.stream.Stream;
25
Yi Tseng7a38f9a2017-06-09 14:36:40 -070026import org.apache.felix.scr.annotations.Activate;
27import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Deactivate;
29import org.apache.felix.scr.annotations.Modified;
30import org.apache.felix.scr.annotations.Property;
31import org.apache.felix.scr.annotations.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
33import org.apache.felix.scr.annotations.Service;
34import org.onlab.packet.ARP;
35import org.onlab.packet.DHCP;
36import org.onlab.packet.DHCP6;
37import org.onlab.packet.IPacket;
38import org.onlab.packet.IPv6;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070039import org.onlab.packet.Ethernet;
40import org.onlab.packet.IPv4;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070041import org.onlab.packet.MacAddress;
42import org.onlab.packet.TpPort;
43import org.onlab.packet.UDP;
44import org.onlab.packet.VlanId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070045import org.onlab.util.Tools;
46import org.onosproject.cfg.ComponentConfigService;
47import org.onosproject.core.ApplicationId;
48import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070049import org.onosproject.dhcprelay.api.DhcpHandler;
50import org.onosproject.dhcprelay.api.DhcpRelayService;
Yi Tsenge72fbb52017-08-02 15:03:31 -070051import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
52import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070053import org.onosproject.dhcprelay.store.DhcpRecord;
54import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tsenge72fbb52017-08-02 15:03:31 -070055import org.onosproject.net.config.Config;
Ray Milkeyfacf2862017-08-03 11:58:29 -070056import org.onosproject.net.intf.Interface;
57import org.onosproject.net.intf.InterfaceService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070058import org.onosproject.net.ConnectPoint;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070059import org.onosproject.net.HostId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070060import org.onosproject.net.config.ConfigFactory;
61import org.onosproject.net.config.NetworkConfigEvent;
62import org.onosproject.net.config.NetworkConfigListener;
63import org.onosproject.net.config.NetworkConfigRegistry;
64import org.onosproject.net.flow.DefaultTrafficSelector;
65import org.onosproject.net.flow.DefaultTrafficTreatment;
66import org.onosproject.net.flow.TrafficSelector;
67import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070068import org.onosproject.net.host.HostService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070069import org.onosproject.net.packet.DefaultOutboundPacket;
70import org.onosproject.net.packet.OutboundPacket;
71import org.onosproject.net.packet.PacketContext;
72import org.onosproject.net.packet.PacketPriority;
73import org.onosproject.net.packet.PacketProcessor;
74import org.onosproject.net.packet.PacketService;
75import org.onosproject.net.provider.ProviderId;
76import org.osgi.service.component.ComponentContext;
77import org.slf4j.Logger;
78import org.slf4j.LoggerFactory;
79
80import com.google.common.collect.ImmutableSet;
81
Yi Tseng7a38f9a2017-06-09 14:36:40 -070082import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070083/**
84 * DHCP Relay Agent Application Component.
85 */
86@Component(immediate = true)
87@Service
88public class DhcpRelayManager implements DhcpRelayService {
Yi Tsenge72fbb52017-08-02 15:03:31 -070089 public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
Yi Tseng51301292017-07-28 13:02:59 -070090 public static final ProviderId PROVIDER_ID = new ProviderId("host", DHCP_RELAY_APP);
Yi Tseng7a38f9a2017-06-09 14:36:40 -070091 public static final String HOST_LOCATION_PROVIDER =
92 "org.onosproject.provider.host.impl.HostLocationProvider";
Yi Tseng325af8c2017-09-01 16:02:56 -070093 public static final String ROUTE_STORE_IMPL =
94 "org.onosproject.routeservice.store.RouteStoreImpl";
Yi Tseng7a38f9a2017-06-09 14:36:40 -070095 private final Logger log = LoggerFactory.getLogger(getClass());
96 private final InternalConfigListener cfgListener = new InternalConfigListener();
97
98 private final Set<ConfigFactory> factories = ImmutableSet.of(
Yi Tsenge72fbb52017-08-02 15:03:31 -070099 new ConfigFactory<ApplicationId, DefaultDhcpRelayConfig>(APP_SUBJECT_FACTORY,
100 DefaultDhcpRelayConfig.class,
101 DefaultDhcpRelayConfig.KEY,
102 true) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700103 @Override
Yi Tsenge72fbb52017-08-02 15:03:31 -0700104 public DefaultDhcpRelayConfig createConfig() {
105 return new DefaultDhcpRelayConfig();
106 }
107 },
108 new ConfigFactory<ApplicationId, IndirectDhcpRelayConfig>(APP_SUBJECT_FACTORY,
109 IndirectDhcpRelayConfig.class,
110 IndirectDhcpRelayConfig.KEY,
111 true) {
112 @Override
113 public IndirectDhcpRelayConfig createConfig() {
114 return new IndirectDhcpRelayConfig();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700115 }
116 }
117 );
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected NetworkConfigRegistry cfgService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected CoreService coreService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected PacketService packetService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected HostService hostService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700132 protected InterfaceService interfaceService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected DhcpRelayStore dhcpRelayStore;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected ComponentConfigService compCfgService;
139
Yi Tseng51301292017-07-28 13:02:59 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
141 target = "(version=4)")
142 protected DhcpHandler v4Handler;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
145 target = "(version=6)")
146 protected DhcpHandler v6Handler;
147
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700148 @Property(name = "arpEnabled", boolValue = true,
Yi Tseng51301292017-07-28 13:02:59 -0700149 label = "Enable Address resolution protocol")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700150 protected boolean arpEnabled = true;
151
152 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700153 private ApplicationId appId;
154
155 @Activate
156 protected void activate(ComponentContext context) {
157 //start the dhcp relay agent
158 appId = coreService.registerApplication(DHCP_RELAY_APP);
159
160 cfgService.addListener(cfgListener);
161 factories.forEach(cfgService::registerConfigFactory);
162 //update the dhcp server configuration.
163 updateConfig();
Yi Tseng51301292017-07-28 13:02:59 -0700164
165 //add the packet processor
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700166 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Yi Tseng51301292017-07-28 13:02:59 -0700167
168 // listen host event for dhcp server or the gateway
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700169 requestDhcpPackets();
170 modified(context);
171
172 // disable dhcp from host location provider
173 compCfgService.preSetProperty(HOST_LOCATION_PROVIDER,
174 "useDhcp", Boolean.FALSE.toString());
Yi Tseng325af8c2017-09-01 16:02:56 -0700175 // Enable distribute route store
176 compCfgService.preSetProperty(ROUTE_STORE_IMPL,
177 "distributed", Boolean.TRUE.toString());
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700178 compCfgService.registerProperties(getClass());
179 log.info("DHCP-RELAY Started");
180 }
181
182 @Deactivate
183 protected void deactivate() {
184 cfgService.removeListener(cfgListener);
185 factories.forEach(cfgService::unregisterConfigFactory);
186 packetService.removeProcessor(dhcpRelayPacketProcessor);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700187 cancelDhcpPackets();
188 cancelArpPackets();
Yi Tseng51301292017-07-28 13:02:59 -0700189
Yi Tsengdfa8ee22017-08-07 12:45:01 -0700190 compCfgService.unregisterProperties(getClass(), false);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700191 log.info("DHCP-RELAY Stopped");
192 }
193
194 @Modified
195 protected void modified(ComponentContext context) {
196 Dictionary<?, ?> properties = context.getProperties();
197 Boolean flag;
198
199 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
200 if (flag != null) {
201 arpEnabled = flag;
202 log.info("Address resolution protocol is {}",
203 arpEnabled ? "enabled" : "disabled");
204 }
205
206 if (arpEnabled) {
207 requestArpPackets();
208 } else {
209 cancelArpPackets();
210 }
211 }
212
Yi Tsenge72fbb52017-08-02 15:03:31 -0700213 /**
214 * Updates DHCP relay app configuration.
215 */
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700216 private void updateConfig() {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700217 DefaultDhcpRelayConfig defaultConfig =
218 cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
219 IndirectDhcpRelayConfig indirectConfig =
220 cfgService.getConfig(appId, IndirectDhcpRelayConfig.class);
221
222 if (defaultConfig != null) {
223 updateConfig(defaultConfig);
224 }
225
226 if (indirectConfig != null) {
227 updateConfig(indirectConfig);
228 }
229 }
230
231 /**
232 * Updates DHCP relay app configuration with given configuration.
233 *
234 * @param config the configuration ot update
235 */
236 private void updateConfig(Config config) {
237 if (config == null) {
238 // Ignore if config is not present
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700239 return;
240 }
Yi Tseng4fa05832017-08-17 13:08:31 -0700241
Yi Tsenge72fbb52017-08-02 15:03:31 -0700242 if (config instanceof IndirectDhcpRelayConfig) {
243 IndirectDhcpRelayConfig indirectConfig = (IndirectDhcpRelayConfig) config;
244 v4Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
245 v6Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
Yi Tseng4fa05832017-08-17 13:08:31 -0700246 } else if (config instanceof DefaultDhcpRelayConfig) {
247 DefaultDhcpRelayConfig defaultConfig = (DefaultDhcpRelayConfig) config;
248 v4Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
249 v6Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
Yi Tsenge72fbb52017-08-02 15:03:31 -0700250 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700251 }
252
253 /**
254 * Request DHCP packet in via PacketService.
255 */
256 private void requestDhcpPackets() {
257 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
258 .matchEthType(Ethernet.TYPE_IPV4)
259 .matchIPProtocol(IPv4.PROTOCOL_UDP)
260 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
261 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
262
263 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
264 .matchEthType(Ethernet.TYPE_IPV4)
265 .matchIPProtocol(IPv4.PROTOCOL_UDP)
266 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
267 packetService.requestPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
268 }
269
270 /**
271 * Cancel requested DHCP packets in via packet service.
272 */
273 private void cancelDhcpPackets() {
274 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
275 .matchEthType(Ethernet.TYPE_IPV4)
276 .matchIPProtocol(IPv4.PROTOCOL_UDP)
277 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
278 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
279
280 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
281 .matchEthType(Ethernet.TYPE_IPV4)
282 .matchIPProtocol(IPv4.PROTOCOL_UDP)
283 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
284 packetService.cancelPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
285 }
286
287 /**
288 * Request ARP packet in via PacketService.
289 */
290 private void requestArpPackets() {
291 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
292 .matchEthType(Ethernet.TYPE_ARP);
293 packetService.requestPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
294 }
295
296 /**
297 * Cancel requested ARP packets in via packet service.
298 */
299 private void cancelArpPackets() {
300 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
301 .matchEthType(Ethernet.TYPE_ARP);
302 packetService.cancelPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
303 }
304
305 @Override
306 public Optional<DhcpRecord> getDhcpRecord(HostId hostId) {
307 return dhcpRelayStore.getDhcpRecord(hostId);
308 }
309
310 @Override
311 public Collection<DhcpRecord> getDhcpRecords() {
312 return dhcpRelayStore.getDhcpRecords();
313 }
314
Yi Tseng13a41a12017-07-26 13:45:01 -0700315 @Override
316 public Optional<MacAddress> getDhcpServerMacAddress() {
Yi Tseng51301292017-07-28 13:02:59 -0700317 return v4Handler.getDhcpConnectMac();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700318 }
319
320 /**
321 * Gets DHCP data from a packet.
322 *
323 * @param packet the packet
324 * @return the DHCP data; empty if it is not a DHCP packet
325 */
326 private Optional<DHCP> findDhcp(Ethernet packet) {
327 return Stream.of(packet)
328 .filter(Objects::nonNull)
329 .map(Ethernet::getPayload)
330 .filter(p -> p instanceof IPv4)
331 .map(IPacket::getPayload)
332 .filter(Objects::nonNull)
333 .filter(p -> p instanceof UDP)
334 .map(IPacket::getPayload)
335 .filter(Objects::nonNull)
336 .filter(p -> p instanceof DHCP)
337 .map(p -> (DHCP) p)
338 .findFirst();
339 }
340
341 /**
342 * Gets DHCPv6 data from a packet.
343 *
344 * @param packet the packet
345 * @return the DHCPv6 data; empty if it is not a DHCPv6 packet
346 */
347 private Optional<DHCP6> findDhcp6(Ethernet packet) {
348 return Stream.of(packet)
349 .filter(Objects::nonNull)
350 .map(Ethernet::getPayload)
351 .filter(p -> p instanceof IPv6)
352 .map(IPacket::getPayload)
353 .filter(Objects::nonNull)
354 .filter(p -> p instanceof UDP)
355 .map(IPacket::getPayload)
356 .filter(Objects::nonNull)
357 .filter(p -> p instanceof DHCP6)
358 .map(p -> (DHCP6) p)
359 .findFirst();
360 }
361
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700362
363 private class DhcpRelayPacketProcessor implements PacketProcessor {
364
365 @Override
366 public void process(PacketContext context) {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700367 // process the packet and get the payload
368 Ethernet packet = context.inPacket().parsed();
369 if (packet == null) {
370 return;
371 }
372
373 findDhcp(packet).ifPresent(dhcpPayload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700374 v4Handler.processDhcpPacket(context, dhcpPayload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700375 });
376
377 findDhcp6(packet).ifPresent(dhcp6Payload -> {
Yi Tseng51301292017-07-28 13:02:59 -0700378 v6Handler.processDhcpPacket(context, dhcp6Payload);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700379 });
380
381 if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
382 ARP arpPacket = (ARP) packet.getPayload();
383 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
384 Set<Interface> interfaces = interfaceService.
385 getInterfacesByPort(context.inPacket().receivedFrom());
386 //ignore the packets if dhcp server interface is not configured on onos.
387 if (interfaces.isEmpty()) {
388 log.warn("server virtual interface not configured");
389 return;
390 }
391 if ((arpPacket.getOpCode() != ARP.OP_REQUEST)) {
392 // handle request only
393 return;
394 }
395 MacAddress interfaceMac = interfaces.stream()
396 .filter(iface -> iface.vlan().equals(vlanId))
397 .map(Interface::mac)
398 .filter(mac -> !mac.equals(MacAddress.NONE))
399 .findFirst()
Yi Tseng51301292017-07-28 13:02:59 -0700400 .orElse(MacAddress.ONOS);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700401 if (interfaceMac == null) {
402 // can't find interface mac address
403 return;
404 }
405 processArpPacket(context, packet, interfaceMac);
406 }
407 }
408
409 /**
410 * Processes the ARP Payload and initiates a reply to the client.
411 *
412 * @param context the packet context
413 * @param packet the ethernet payload
414 * @param replyMac mac address to be replied
415 */
416 private void processArpPacket(PacketContext context, Ethernet packet, MacAddress replyMac) {
417 ARP arpPacket = (ARP) packet.getPayload();
418 ARP arpReply = (ARP) arpPacket.clone();
419 arpReply.setOpCode(ARP.OP_REPLY);
420
421 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
422 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
423 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
424 arpReply.setSenderHardwareAddress(replyMac.toBytes());
425
426 // Ethernet Frame.
427 Ethernet ethReply = new Ethernet();
428 ethReply.setSourceMACAddress(replyMac.toBytes());
429 ethReply.setDestinationMACAddress(packet.getSourceMAC());
430 ethReply.setEtherType(Ethernet.TYPE_ARP);
431 ethReply.setVlanID(packet.getVlanID());
432 ethReply.setPayload(arpReply);
433
434 ConnectPoint targetPort = context.inPacket().receivedFrom();
435 TrafficTreatment t = DefaultTrafficTreatment.builder()
436 .setOutput(targetPort.port()).build();
437 OutboundPacket o = new DefaultOutboundPacket(
438 targetPort.deviceId(), t, ByteBuffer.wrap(ethReply.serialize()));
439 if (log.isTraceEnabled()) {
440 log.trace("Relaying ARP packet {} to {}", packet, targetPort);
441 }
442 packetService.emit(o);
443 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700444 }
445
446 /**
447 * Listener for network config events.
448 */
449 private class InternalConfigListener implements NetworkConfigListener {
450 @Override
451 public void event(NetworkConfigEvent event) {
Yi Tsenge72fbb52017-08-02 15:03:31 -0700452 if (event.type() != NetworkConfigEvent.Type.CONFIG_ADDED &&
453 event.type() != NetworkConfigEvent.Type.CONFIG_UPDATED) {
454 // Ignore unhandled event type
455 return;
456 }
457 if (!event.configClass().equals(DefaultDhcpRelayConfig.class) &&
458 !event.configClass().equals(IndirectDhcpRelayConfig.class)) {
459 // Ignore unhandled config type
460 return;
461 }
462 event.config().ifPresent(config -> {
463 updateConfig(config);
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700464 log.info("Reconfigured");
Yi Tsenge72fbb52017-08-02 15:03:31 -0700465 });
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700466 }
467 }
468}