blob: ee995d09678e1d2bc74bdf4d3a365033a8604a2b [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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
samanwita palf66ed8a2015-07-21 15:45:33 -070023import org.apache.felix.scr.annotations.Service;
samanwita pal18696f62015-07-17 13:15:43 -070024import org.onlab.packet.DHCP;
25import org.onlab.packet.DHCPOption;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IPv4;
28import org.onlab.packet.Ip4Address;
29import org.onlab.packet.MacAddress;
30import org.onlab.packet.UDP;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
samanwita palf66ed8a2015-07-21 15:45:33 -070033import org.onosproject.dhcpserver.DHCPService;
34import org.onosproject.dhcpserver.DHCPStore;
samanwita pal18696f62015-07-17 13:15:43 -070035import org.onosproject.net.ConnectPoint;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.packet.DefaultOutboundPacket;
41import org.onosproject.net.packet.PacketContext;
42import org.onosproject.net.packet.PacketPriority;
43import org.onosproject.net.packet.PacketProcessor;
44import org.onosproject.net.packet.PacketService;
45import org.slf4j.Logger;
46import org.slf4j.LoggerFactory;
47
48import java.nio.ByteBuffer;
49import java.util.ArrayList;
50import java.util.List;
samanwita palf66ed8a2015-07-21 15:45:33 -070051import java.util.Map;
samanwita pal18696f62015-07-17 13:15:43 -070052
53import static org.onlab.packet.MacAddress.valueOf;
54
55/**
56 * Skeletal ONOS DHCP Server application.
57 */
58@Component(immediate = true)
samanwita palf66ed8a2015-07-21 15:45:33 -070059@Service
60public class DHCPManager implements DHCPService {
samanwita pal18696f62015-07-17 13:15:43 -070061
62 private final Logger log = LoggerFactory.getLogger(getClass());
63
64 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 protected PacketService packetService;
66
67 private DHCPPacketProcessor processor = new DHCPPacketProcessor();
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected CoreService coreService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected DHCPStore dhcpStore;
74
75 private ApplicationId appId;
76
77 // TODO Make the hardcoded values configurable.
78
79 private static final String MY_IP = "10.0.0.2";
80
81 private static final MacAddress MY_MAC = valueOf("4f:4f:4f:4f:4f:4f");
82
83 /**
84 * leaseTime - 10 mins or 600s.
85 * renewalTime - 5 mins or 300s.
86 * rebindingTime - 6 mins or 360s.
87 */
88
89 private static int leaseTime = 600;
90
91 private static int renewalTime = 300;
92
93 private static int rebindingTime = 360;
94
95 private static byte packetTTL = (byte) 127;
96
97 private static String subnetMask = "255.0.0.0";
98
99 private static String broadcastAddress = "10.255.255.255";
100
101 @Activate
102 protected void activate() {
103 // start the dhcp server
104 appId = coreService.registerApplication("org.onosproject.dhcpserver");
105
106 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 10);
107 requestPackets();
108 log.info("Started");
109 }
110
111 @Deactivate
112 protected void deactivate() {
113 packetService.removeProcessor(processor);
114
115 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
116 .matchEthType(Ethernet.TYPE_IPV4)
117 .matchIPProtocol(IPv4.PROTOCOL_UDP)
118 .matchUdpDst(UDP.DHCP_SERVER_PORT)
119 .matchUdpSrc(UDP.DHCP_CLIENT_PORT);
120
121 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
122 log.info("Stopped");
123 }
124
125 /**
126 * Request packet in via PacketService.
127 */
128 private void requestPackets() {
129
130 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
131 .matchEthType(Ethernet.TYPE_IPV4)
132 .matchIPProtocol(IPv4.PROTOCOL_UDP)
133 .matchUdpDst(UDP.DHCP_SERVER_PORT)
134 .matchUdpSrc(UDP.DHCP_CLIENT_PORT);
135
136 packetService.requestPackets(selectorServer.build(),
137 PacketPriority.CONTROL, appId);
138 }
139
samanwita palf66ed8a2015-07-21 15:45:33 -0700140 @Override
141 public Map<MacAddress, Ip4Address> listMapping() {
142
143 return dhcpStore.listMapping();
144 }
145
146 @Override
147 public int getLeaseTime() {
148 return leaseTime;
149 }
150
151 @Override
152 public int getRenewalTime() {
153 return renewalTime;
154 }
155
156 @Override
157 public int getRebindingTime() {
158 return rebindingTime;
159 }
160
161 @Override
162 public boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress) {
163 return dhcpStore.assignStaticIP(macID, ipAddress);
164 }
165
166 @Override
167 public boolean removeStaticMapping(MacAddress macID) {
168 return dhcpStore.removeStaticIP(macID);
169 }
170
171 @Override
172 public Iterable<Ip4Address> getAvailableIPs() {
173 return dhcpStore.getAvailableIPs();
174 }
175
samanwita pal18696f62015-07-17 13:15:43 -0700176 private class DHCPPacketProcessor implements PacketProcessor {
177
178 /**
179 * Builds the DHCP Reply packet.
180 *
181 * @param packet the incoming Ethernet frame
182 * @param ipOffered the IP offered by the DHCP Server
183 * @param outgoingMessageType the message type of the outgoing packet
184 * @return the Ethernet reply frame
185 */
186 private Ethernet buildReply(Ethernet packet, String ipOffered, byte outgoingMessageType) {
187 Ip4Address myIPAddress = Ip4Address.valueOf(MY_IP);
188 Ip4Address ipAddress;
189
190 // Ethernet Frame.
191 Ethernet ethReply = new Ethernet();
192 ethReply.setSourceMACAddress(MY_MAC);
193 ethReply.setDestinationMACAddress(packet.getSourceMAC());
194 ethReply.setEtherType(Ethernet.TYPE_IPV4);
195 ethReply.setVlanID(packet.getVlanID());
196
197 // IP Packet
198 IPv4 ipv4Packet = (IPv4) packet.getPayload();
199 IPv4 ipv4Reply = new IPv4();
200 ipv4Reply.setSourceAddress(myIPAddress.toInt());
201 ipAddress = Ip4Address.valueOf(ipOffered);
202 ipv4Reply.setDestinationAddress(ipAddress.toInt());
203 ipv4Reply.setTtl(packetTTL);
204
205 // UDP Datagram.
206 UDP udpPacket = (UDP) ipv4Packet.getPayload();
207 UDP udpReply = new UDP();
208 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
209 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
210
211 // DHCP Payload.
212 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
213 DHCP dhcpReply = new DHCP();
214 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
215
216 ipAddress = Ip4Address.valueOf(ipOffered);
217 dhcpReply.setYourIPAddress(ipAddress.toInt());
218 dhcpReply.setServerIPAddress(myIPAddress.toInt());
219
220 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
221 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
222 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
223 dhcpReply.setHardwareAddressLength((byte) 6);
224
225 // DHCP Options.
226 DHCPOption option = new DHCPOption();
227 List<DHCPOption> optionList = new ArrayList<>();
228
229 // DHCP Message Type.
230 option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
231 option.setLength((byte) 1);
232 byte[] optionData = {outgoingMessageType};
233 option.setData(optionData);
234 optionList.add(option);
235
236 // DHCP Server Identifier.
237 option = new DHCPOption();
238 option.setCode(DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue());
239 option.setLength((byte) 4);
240 option.setData(myIPAddress.toOctets());
241 optionList.add(option);
242
243 // IP Address Lease Time.
244 option = new DHCPOption();
245 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
246 option.setLength((byte) 4);
247 option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
248 optionList.add(option);
249
250 // IP Address Renewal Time.
251 option = new DHCPOption();
252 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
253 option.setLength((byte) 4);
254 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
255 optionList.add(option);
256
257 // IP Address Rebinding Time.
258 option = new DHCPOption();
259 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
260 option.setLength((byte) 4);
261 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
262 optionList.add(option);
263
264 // Subnet Mask.
265 option = new DHCPOption();
266 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
267 option.setLength((byte) 4);
268 ipAddress = Ip4Address.valueOf(subnetMask);
269 option.setData(ipAddress.toOctets());
270 optionList.add(option);
271
272 // Broadcast Address.
273 option = new DHCPOption();
274 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
275 option.setLength((byte) 4);
276 ipAddress = Ip4Address.valueOf(broadcastAddress);
277 option.setData(ipAddress.toOctets());
278 optionList.add(option);
279
280 // Router Address.
281 option = new DHCPOption();
282 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
283 option.setLength((byte) 4);
284 option.setData(myIPAddress.toOctets());
285 optionList.add(option);
286
287 // DNS Server Address.
288 option = new DHCPOption();
289 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
290 option.setLength((byte) 4);
291 option.setData(myIPAddress.toOctets());
292 optionList.add(option);
293
294 // End Option.
295 option = new DHCPOption();
296 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
297 option.setLength((byte) 1);
298 optionList.add(option);
299
300 dhcpReply.setOptions(optionList);
301
302 udpReply.setPayload(dhcpReply);
303 ipv4Reply.setPayload(udpReply);
304 ethReply.setPayload(ipv4Reply);
305
306 return ethReply;
307 }
308
309 /**
310 * Sends the Ethernet reply frame via the Packet Service.
311 *
312 * @param context the context of the incoming frame
313 * @param reply the Ethernet reply frame
314 */
315 private void sendReply(PacketContext context, Ethernet reply) {
316 if (reply != null) {
317 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
318 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
319 builder.setOutput(sourcePoint.port());
320
321 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
322 builder.build(), ByteBuffer.wrap(reply.serialize())));
323 }
324 }
325
326 /**
327 * Processes the DHCP Payload and initiates a reply to the client.
328 *
329 * @param context context of the incoming message
330 * @param dhcpPayload the extracted DHCP payload
331 */
332 private void processDHCPPacket(PacketContext context, DHCP dhcpPayload) {
333
334 Ethernet packet = context.inPacket().parsed();
335 boolean flagIfRequestedIP = false;
336 boolean flagIfServerIP = false;
337 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
338 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
339
340 if (dhcpPayload != null) {
341
342 // TODO Convert this to enum value.
343 byte incomingPacketType = 0;
344 for (DHCPOption option : dhcpPayload.getOptions()) {
345 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) {
346 byte[] data = option.getData();
347 incomingPacketType = data[0];
348 }
349 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue()) {
350 byte[] data = option.getData();
351 requestedIP = Ip4Address.valueOf(data);
352 flagIfRequestedIP = true;
353 }
354 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue()) {
355 byte[] data = option.getData();
356 serverIP = Ip4Address.valueOf(data);
357 flagIfServerIP = true;
358 }
359 }
360
361 String ipOffered = "";
362 DHCP.DHCPMessageType outgoingPacketType;
363 MacAddress clientMAC = new MacAddress(dhcpPayload.getClientHardwareAddress());
364
365 if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Discover.getValue()) {
366
367 outgoingPacketType = DHCP.DHCPMessageType.MessageType_Offer;
samanwita palf09c09e2015-07-22 16:06:42 -0700368 ipOffered = dhcpStore.suggestIP(clientMAC, requestedIP).toString();
samanwita pal18696f62015-07-17 13:15:43 -0700369
370 Ethernet ethReply = buildReply(packet, ipOffered, outgoingPacketType.getValue());
371 sendReply(context, ethReply);
372
373 } else if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Request.getValue()) {
374
375 outgoingPacketType = DHCP.DHCPMessageType.MessageType_ACK;
376
377 if (flagIfServerIP && flagIfRequestedIP) {
378 // SELECTING state
379 if (MY_IP.equals(serverIP.toString()) &&
380 dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) {
381
382 Ethernet ethReply = buildReply(packet, requestedIP.toString(),
383 outgoingPacketType.getValue());
384 sendReply(context, ethReply);
385 }
386 } else if (flagIfRequestedIP) {
387 // INIT-REBOOT state
388 if (dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) {
389 Ethernet ethReply = buildReply(packet, requestedIP.toString(),
390 outgoingPacketType.getValue());
391 sendReply(context, ethReply);
392 }
393 } else {
394 // RENEWING and REBINDING state
395 int ciaadr = dhcpPayload.getClientIPAddress();
396 if (ciaadr != 0) {
397 Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
398 if (dhcpStore.assignIP(clientMAC, clientIaddr, leaseTime)) {
399 Ethernet ethReply = buildReply(packet, clientIaddr.toString(),
400 outgoingPacketType.getValue());
401 sendReply(context, ethReply);
402 }
403 }
404 }
405 } else if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Release.getValue()) {
406
407 dhcpStore.releaseIP(clientMAC);
408 }
409 }
410 }
411
412 @Override
413 public void process(PacketContext context) {
414
415 // Stop processing if the packet has been handled, since we
416 // can't do any more to it.
417 if (context.isHandled()) {
418 return;
419 }
420
421 Ethernet packet = context.inPacket().parsed();
422 if (packet == null) {
423 return;
424 }
425
426 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
427 IPv4 ipv4Packet = (IPv4) packet.getPayload();
428
429 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
430 UDP udpPacket = (UDP) ipv4Packet.getPayload();
431
432 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
433 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
434 // This is meant for the dhcp server so process the packet here.
435
436 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
437 processDHCPPacket(context, dhcpPayload);
438 }
439 }
440 }
441 }
442 }
443}