blob: 39cf17ddcdb3667bf6b9c7e090d5c80de39b70af [file] [log] [blame]
samanwita pal18696f62015-07-17 13:15:43 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
samanwita palf66ed8a2015-07-21 15:45:33 -070016package org.onosproject.dhcpserver.impl;
samanwita pal18696f62015-07-17 13:15:43 -070017
samanwita pal775f6192015-07-28 10:10:13 -070018import com.google.common.collect.ImmutableSet;
samanwita pal18696f62015-07-17 13:15:43 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
samanwita palf66ed8a2015-07-21 15:45:33 -070024import org.apache.felix.scr.annotations.Service;
samanwita pal18696f62015-07-17 13:15:43 -070025import org.onlab.packet.DHCP;
26import org.onlab.packet.DHCPOption;
27import org.onlab.packet.Ethernet;
28import org.onlab.packet.IPv4;
29import org.onlab.packet.Ip4Address;
30import org.onlab.packet.MacAddress;
31import org.onlab.packet.UDP;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
samanwita palf66ed8a2015-07-21 15:45:33 -070034import org.onosproject.dhcpserver.DHCPService;
35import org.onosproject.dhcpserver.DHCPStore;
samanwita pal775f6192015-07-28 10:10:13 -070036import org.onosproject.incubator.net.config.ConfigFactory;
37import org.onosproject.incubator.net.config.NetworkConfigEvent;
38import org.onosproject.incubator.net.config.NetworkConfigListener;
39import org.onosproject.incubator.net.config.NetworkConfigRegistry;
samanwita pal18696f62015-07-17 13:15:43 -070040import org.onosproject.net.ConnectPoint;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.packet.DefaultOutboundPacket;
46import org.onosproject.net.packet.PacketContext;
47import org.onosproject.net.packet.PacketPriority;
48import org.onosproject.net.packet.PacketProcessor;
49import org.onosproject.net.packet.PacketService;
50import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
53import java.nio.ByteBuffer;
54import java.util.ArrayList;
55import java.util.List;
samanwita palf66ed8a2015-07-21 15:45:33 -070056import java.util.Map;
samanwita pal775f6192015-07-28 10:10:13 -070057import java.util.Set;
samanwita pal18696f62015-07-17 13:15:43 -070058
59import static org.onlab.packet.MacAddress.valueOf;
samanwita pal775f6192015-07-28 10:10:13 -070060import static org.onosproject.incubator.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
samanwita pal18696f62015-07-17 13:15:43 -070061
62/**
63 * Skeletal ONOS DHCP Server application.
64 */
65@Component(immediate = true)
samanwita palf66ed8a2015-07-21 15:45:33 -070066@Service
67public class DHCPManager implements DHCPService {
samanwita pal18696f62015-07-17 13:15:43 -070068
69 private final Logger log = LoggerFactory.getLogger(getClass());
70
samanwita pal775f6192015-07-28 10:10:13 -070071 private final NetworkConfigListener cfgListener = new InternalConfigListener();
72
73 private final Set<ConfigFactory> factories = ImmutableSet.of(
74 new ConfigFactory<ApplicationId, DHCPConfig>(APP_SUBJECT_FACTORY,
75 DHCPConfig.class,
76 "dhcp") {
77 @Override
78 public DHCPConfig createConfig() {
79 return new DHCPConfig();
80 }
81 },
82 new ConfigFactory<ApplicationId, DHCPStoreConfig>(APP_SUBJECT_FACTORY,
83 DHCPStoreConfig.class,
84 "dhcpstore") {
85 @Override
86 public DHCPStoreConfig createConfig() {
87 return new DHCPStoreConfig();
88 }
89 }
90 );
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected NetworkConfigRegistry cfgService;
93
samanwita pal18696f62015-07-17 13:15:43 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected PacketService packetService;
96
97 private DHCPPacketProcessor processor = new DHCPPacketProcessor();
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected DHCPStore dhcpStore;
104
105 private ApplicationId appId;
106
samanwita pal775f6192015-07-28 10:10:13 -0700107 // Hardcoded values are default values.
samanwita pal18696f62015-07-17 13:15:43 -0700108
samanwita pal775f6192015-07-28 10:10:13 -0700109 private static String myIP = "10.0.0.2";
samanwita pal18696f62015-07-17 13:15:43 -0700110
samanwita pal775f6192015-07-28 10:10:13 -0700111 private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
samanwita pal18696f62015-07-17 13:15:43 -0700112
113 /**
114 * leaseTime - 10 mins or 600s.
115 * renewalTime - 5 mins or 300s.
116 * rebindingTime - 6 mins or 360s.
117 */
118
119 private static int leaseTime = 600;
120
121 private static int renewalTime = 300;
122
123 private static int rebindingTime = 360;
124
125 private static byte packetTTL = (byte) 127;
126
127 private static String subnetMask = "255.0.0.0";
128
129 private static String broadcastAddress = "10.255.255.255";
130
samanwita pal775f6192015-07-28 10:10:13 -0700131 private static Ip4Address startIPRange = Ip4Address.valueOf("10.1.0.140");
132
133 private static Ip4Address endIPRange = Ip4Address.valueOf("10.1.0.160");
134
samanwita pal18696f62015-07-17 13:15:43 -0700135 @Activate
136 protected void activate() {
137 // start the dhcp server
138 appId = coreService.registerApplication("org.onosproject.dhcpserver");
139
samanwita pal775f6192015-07-28 10:10:13 -0700140 cfgService.addListener(cfgListener);
141 factories.forEach(cfgService::registerConfigFactory);
samanwita pal18696f62015-07-17 13:15:43 -0700142 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 10);
143 requestPackets();
samanwita pal775f6192015-07-28 10:10:13 -0700144 dhcpStore.populateIPPoolfromRange(startIPRange, endIPRange);
samanwita pal18696f62015-07-17 13:15:43 -0700145 log.info("Started");
146 }
147
148 @Deactivate
149 protected void deactivate() {
samanwita pal775f6192015-07-28 10:10:13 -0700150 cfgService.removeListener(cfgListener);
151 factories.forEach(cfgService::unregisterConfigFactory);
samanwita pal18696f62015-07-17 13:15:43 -0700152 packetService.removeProcessor(processor);
153
154 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
155 .matchEthType(Ethernet.TYPE_IPV4)
156 .matchIPProtocol(IPv4.PROTOCOL_UDP)
157 .matchUdpDst(UDP.DHCP_SERVER_PORT)
158 .matchUdpSrc(UDP.DHCP_CLIENT_PORT);
159
160 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
161 log.info("Stopped");
162 }
163
164 /**
165 * Request packet in via PacketService.
166 */
167 private void requestPackets() {
168
169 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
170 .matchEthType(Ethernet.TYPE_IPV4)
171 .matchIPProtocol(IPv4.PROTOCOL_UDP)
172 .matchUdpDst(UDP.DHCP_SERVER_PORT)
173 .matchUdpSrc(UDP.DHCP_CLIENT_PORT);
174
175 packetService.requestPackets(selectorServer.build(),
176 PacketPriority.CONTROL, appId);
177 }
178
samanwita palf66ed8a2015-07-21 15:45:33 -0700179 @Override
180 public Map<MacAddress, Ip4Address> listMapping() {
181
182 return dhcpStore.listMapping();
183 }
184
185 @Override
186 public int getLeaseTime() {
187 return leaseTime;
188 }
189
190 @Override
191 public int getRenewalTime() {
192 return renewalTime;
193 }
194
195 @Override
196 public int getRebindingTime() {
197 return rebindingTime;
198 }
199
200 @Override
201 public boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress) {
202 return dhcpStore.assignStaticIP(macID, ipAddress);
203 }
204
205 @Override
206 public boolean removeStaticMapping(MacAddress macID) {
207 return dhcpStore.removeStaticIP(macID);
208 }
209
210 @Override
211 public Iterable<Ip4Address> getAvailableIPs() {
212 return dhcpStore.getAvailableIPs();
213 }
214
samanwita pal18696f62015-07-17 13:15:43 -0700215 private class DHCPPacketProcessor implements PacketProcessor {
216
217 /**
218 * Builds the DHCP Reply packet.
219 *
220 * @param packet the incoming Ethernet frame
221 * @param ipOffered the IP offered by the DHCP Server
222 * @param outgoingMessageType the message type of the outgoing packet
223 * @return the Ethernet reply frame
224 */
225 private Ethernet buildReply(Ethernet packet, String ipOffered, byte outgoingMessageType) {
samanwita pal775f6192015-07-28 10:10:13 -0700226 Ip4Address myIPAddress = Ip4Address.valueOf(myIP);
samanwita pal18696f62015-07-17 13:15:43 -0700227 Ip4Address ipAddress;
228
229 // Ethernet Frame.
230 Ethernet ethReply = new Ethernet();
samanwita pal775f6192015-07-28 10:10:13 -0700231 ethReply.setSourceMACAddress(myMAC);
samanwita pal18696f62015-07-17 13:15:43 -0700232 ethReply.setDestinationMACAddress(packet.getSourceMAC());
233 ethReply.setEtherType(Ethernet.TYPE_IPV4);
234 ethReply.setVlanID(packet.getVlanID());
235
236 // IP Packet
237 IPv4 ipv4Packet = (IPv4) packet.getPayload();
238 IPv4 ipv4Reply = new IPv4();
239 ipv4Reply.setSourceAddress(myIPAddress.toInt());
240 ipAddress = Ip4Address.valueOf(ipOffered);
241 ipv4Reply.setDestinationAddress(ipAddress.toInt());
242 ipv4Reply.setTtl(packetTTL);
243
244 // UDP Datagram.
245 UDP udpPacket = (UDP) ipv4Packet.getPayload();
246 UDP udpReply = new UDP();
247 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
248 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
249
250 // DHCP Payload.
251 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
252 DHCP dhcpReply = new DHCP();
253 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
254
255 ipAddress = Ip4Address.valueOf(ipOffered);
256 dhcpReply.setYourIPAddress(ipAddress.toInt());
257 dhcpReply.setServerIPAddress(myIPAddress.toInt());
258
259 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
260 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
261 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
262 dhcpReply.setHardwareAddressLength((byte) 6);
263
264 // DHCP Options.
265 DHCPOption option = new DHCPOption();
266 List<DHCPOption> optionList = new ArrayList<>();
267
268 // DHCP Message Type.
269 option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
270 option.setLength((byte) 1);
271 byte[] optionData = {outgoingMessageType};
272 option.setData(optionData);
273 optionList.add(option);
274
275 // DHCP Server Identifier.
276 option = new DHCPOption();
277 option.setCode(DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue());
278 option.setLength((byte) 4);
279 option.setData(myIPAddress.toOctets());
280 optionList.add(option);
281
282 // IP Address Lease Time.
283 option = new DHCPOption();
284 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
285 option.setLength((byte) 4);
286 option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
287 optionList.add(option);
288
289 // IP Address Renewal Time.
290 option = new DHCPOption();
291 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
292 option.setLength((byte) 4);
293 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
294 optionList.add(option);
295
296 // IP Address Rebinding Time.
297 option = new DHCPOption();
298 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
299 option.setLength((byte) 4);
300 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
301 optionList.add(option);
302
303 // Subnet Mask.
304 option = new DHCPOption();
305 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
306 option.setLength((byte) 4);
307 ipAddress = Ip4Address.valueOf(subnetMask);
308 option.setData(ipAddress.toOctets());
309 optionList.add(option);
310
311 // Broadcast Address.
312 option = new DHCPOption();
313 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
314 option.setLength((byte) 4);
315 ipAddress = Ip4Address.valueOf(broadcastAddress);
316 option.setData(ipAddress.toOctets());
317 optionList.add(option);
318
319 // Router Address.
320 option = new DHCPOption();
321 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
322 option.setLength((byte) 4);
323 option.setData(myIPAddress.toOctets());
324 optionList.add(option);
325
326 // DNS Server Address.
327 option = new DHCPOption();
328 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
329 option.setLength((byte) 4);
330 option.setData(myIPAddress.toOctets());
331 optionList.add(option);
332
333 // End Option.
334 option = new DHCPOption();
335 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
336 option.setLength((byte) 1);
337 optionList.add(option);
338
339 dhcpReply.setOptions(optionList);
340
341 udpReply.setPayload(dhcpReply);
342 ipv4Reply.setPayload(udpReply);
343 ethReply.setPayload(ipv4Reply);
344
345 return ethReply;
346 }
347
348 /**
349 * Sends the Ethernet reply frame via the Packet Service.
350 *
351 * @param context the context of the incoming frame
352 * @param reply the Ethernet reply frame
353 */
354 private void sendReply(PacketContext context, Ethernet reply) {
355 if (reply != null) {
356 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
357 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
358 builder.setOutput(sourcePoint.port());
359
360 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
361 builder.build(), ByteBuffer.wrap(reply.serialize())));
362 }
363 }
364
365 /**
366 * Processes the DHCP Payload and initiates a reply to the client.
367 *
368 * @param context context of the incoming message
369 * @param dhcpPayload the extracted DHCP payload
370 */
371 private void processDHCPPacket(PacketContext context, DHCP dhcpPayload) {
372
373 Ethernet packet = context.inPacket().parsed();
374 boolean flagIfRequestedIP = false;
375 boolean flagIfServerIP = false;
376 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
377 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
378
379 if (dhcpPayload != null) {
380
381 // TODO Convert this to enum value.
382 byte incomingPacketType = 0;
383 for (DHCPOption option : dhcpPayload.getOptions()) {
384 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) {
385 byte[] data = option.getData();
386 incomingPacketType = data[0];
387 }
388 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue()) {
389 byte[] data = option.getData();
390 requestedIP = Ip4Address.valueOf(data);
391 flagIfRequestedIP = true;
392 }
393 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue()) {
394 byte[] data = option.getData();
395 serverIP = Ip4Address.valueOf(data);
396 flagIfServerIP = true;
397 }
398 }
399
400 String ipOffered = "";
401 DHCP.DHCPMessageType outgoingPacketType;
402 MacAddress clientMAC = new MacAddress(dhcpPayload.getClientHardwareAddress());
403
404 if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Discover.getValue()) {
405
406 outgoingPacketType = DHCP.DHCPMessageType.MessageType_Offer;
samanwita palf09c09e2015-07-22 16:06:42 -0700407 ipOffered = dhcpStore.suggestIP(clientMAC, requestedIP).toString();
samanwita pal18696f62015-07-17 13:15:43 -0700408
409 Ethernet ethReply = buildReply(packet, ipOffered, outgoingPacketType.getValue());
410 sendReply(context, ethReply);
411
412 } else if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Request.getValue()) {
413
414 outgoingPacketType = DHCP.DHCPMessageType.MessageType_ACK;
415
416 if (flagIfServerIP && flagIfRequestedIP) {
417 // SELECTING state
samanwita pal775f6192015-07-28 10:10:13 -0700418 if (myIP.equals(serverIP.toString()) &&
samanwita pal18696f62015-07-17 13:15:43 -0700419 dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) {
420
421 Ethernet ethReply = buildReply(packet, requestedIP.toString(),
422 outgoingPacketType.getValue());
423 sendReply(context, ethReply);
424 }
425 } else if (flagIfRequestedIP) {
426 // INIT-REBOOT state
427 if (dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) {
428 Ethernet ethReply = buildReply(packet, requestedIP.toString(),
429 outgoingPacketType.getValue());
430 sendReply(context, ethReply);
431 }
432 } else {
433 // RENEWING and REBINDING state
434 int ciaadr = dhcpPayload.getClientIPAddress();
435 if (ciaadr != 0) {
436 Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
437 if (dhcpStore.assignIP(clientMAC, clientIaddr, leaseTime)) {
438 Ethernet ethReply = buildReply(packet, clientIaddr.toString(),
439 outgoingPacketType.getValue());
440 sendReply(context, ethReply);
441 }
442 }
443 }
444 } else if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Release.getValue()) {
445
446 dhcpStore.releaseIP(clientMAC);
447 }
448 }
449 }
450
451 @Override
452 public void process(PacketContext context) {
453
454 // Stop processing if the packet has been handled, since we
455 // can't do any more to it.
456 if (context.isHandled()) {
457 return;
458 }
459
460 Ethernet packet = context.inPacket().parsed();
461 if (packet == null) {
462 return;
463 }
464
465 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
466 IPv4 ipv4Packet = (IPv4) packet.getPayload();
467
468 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
469 UDP udpPacket = (UDP) ipv4Packet.getPayload();
470
471 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
472 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
473 // This is meant for the dhcp server so process the packet here.
474
475 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
476 processDHCPPacket(context, dhcpPayload);
477 }
478 }
479 }
480 }
481 }
samanwita pal775f6192015-07-28 10:10:13 -0700482
483 private class InternalConfigListener implements NetworkConfigListener {
484
485 /**
486 * Reconfigures the DHCP Server according to the configuration parameters passed.
487 *
488 * @param cfg configuration object
489 */
490 private void reconfigureNetwork(DHCPConfig cfg) {
491
492 if (cfg.ip() != null) {
493 myIP = cfg.ip();
494 }
495 if (cfg.mac() != null) {
496 myMAC = MacAddress.valueOf(cfg.mac());
497 }
498 if (cfg.subnetMask() != null) {
499 subnetMask = cfg.subnetMask();
500 }
501 if (cfg.broadcastAddress() != null) {
502 broadcastAddress = cfg.broadcastAddress();
503 }
504 if (cfg.ttl() != null) {
505 packetTTL = Byte.valueOf(cfg.ttl());
506 }
507 if (cfg.leaseTime() != null) {
508 leaseTime = Integer.valueOf(cfg.leaseTime());
509 }
510 if (cfg.renewTime() != null) {
511 renewalTime = Integer.valueOf(cfg.renewTime());
512 }
513 if (cfg.rebindTime() != null) {
514 rebindingTime = Integer.valueOf(cfg.rebindTime());
515 }
516 }
517
518 /**
519 * Reconfigures the DHCP Store according to the configuration parameters passed.
520 *
521 * @param cfg configuration object
522 */
523 private void reconfigureStore(DHCPStoreConfig cfg) {
524
525 if (cfg.defaultTimeout() != null) {
526 dhcpStore.setDefaultTimeoutForPurge(Integer.valueOf(cfg.defaultTimeout()));
527 }
528 if (cfg.timerDelay() != null) {
529 dhcpStore.setTimerDelay(Integer.valueOf(cfg.defaultTimeout()));
530 }
531 if ((cfg.startIP() != null) && (cfg.endIP() != null)) {
532 startIPRange = Ip4Address.valueOf(cfg.startIP());
533 endIPRange = Ip4Address.valueOf(cfg.endIP());
534 dhcpStore.populateIPPoolfromRange(startIPRange, endIPRange);
535 }
536 }
537
538 @Override
539 public void event(NetworkConfigEvent event) {
540
541 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
542 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) {
543 if (event.configClass().equals(DHCPConfig.class)) {
544 DHCPConfig cfg = cfgService.getConfig(appId, DHCPConfig.class);
545 reconfigureNetwork(cfg);
546 log.info("Reconfigured Manager");
547 }
548 if (event.configClass().equals(DHCPStoreConfig.class)) {
549 DHCPStoreConfig cfg = cfgService.getConfig(appId, DHCPStoreConfig.class);
550 reconfigureStore(cfg);
551 log.info("Reconfigured Store");
552 }
553
554 }
555 log.info("Reconfigured");
556 }
557 }
samanwita pal18696f62015-07-17 13:15:43 -0700558}