blob: a1707e0bebd71959adef6a72cae9960cd719e139 [file] [log] [blame]
samanwita palf28207b2015-09-04 10:41:56 -07001/*
samanwita pala3d1d1c2015-09-25 11:50:15 -07002 * Copyright 2015 Open Networking Laboratory
samanwita palf28207b2015-09-04 10:41:56 -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.dhcp.impl;
17
18import com.google.common.collect.ImmutableSet;
danielcd9deed2015-10-30 17:16:16 +090019import com.google.common.collect.Lists;
samanwita palf28207b2015-09-04 10:41:56 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
samanwita pal2a313402015-09-14 16:03:22 -070026import org.jboss.netty.util.Timeout;
27import org.jboss.netty.util.TimerTask;
samanwita palf28207b2015-09-04 10:41:56 -070028import org.onlab.packet.ARP;
29import org.onlab.packet.DHCP;
30import org.onlab.packet.DHCPOption;
samanwita pal8969cbe2015-09-04 13:31:30 -070031import org.onlab.packet.DHCPPacketType;
samanwita palf28207b2015-09-04 10:41:56 -070032import org.onlab.packet.Ethernet;
33import org.onlab.packet.IPv4;
34import org.onlab.packet.Ip4Address;
35import org.onlab.packet.IpAddress;
36import org.onlab.packet.MacAddress;
37import org.onlab.packet.TpPort;
38import org.onlab.packet.UDP;
39import org.onlab.packet.VlanId;
samanwita pal2a313402015-09-14 16:03:22 -070040import org.onlab.util.Timer;
samanwita palf28207b2015-09-04 10:41:56 -070041import org.onosproject.core.ApplicationId;
42import org.onosproject.core.CoreService;
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070043import org.onosproject.dhcp.DhcpService;
44import org.onosproject.dhcp.DhcpStore;
45import org.onosproject.dhcp.IpAssignment;
samanwita palf28207b2015-09-04 10:41:56 -070046import org.onosproject.net.ConnectPoint;
47import org.onosproject.net.Host;
48import org.onosproject.net.HostId;
49import org.onosproject.net.HostLocation;
samanwita pal2a313402015-09-14 16:03:22 -070050import org.onosproject.net.config.ConfigFactory;
51import org.onosproject.net.config.NetworkConfigEvent;
52import org.onosproject.net.config.NetworkConfigListener;
53import org.onosproject.net.config.NetworkConfigRegistry;
samanwita palf28207b2015-09-04 10:41:56 -070054import org.onosproject.net.flow.DefaultTrafficSelector;
55import org.onosproject.net.flow.DefaultTrafficTreatment;
56import org.onosproject.net.flow.TrafficSelector;
57import org.onosproject.net.flow.TrafficTreatment;
58import org.onosproject.net.host.DefaultHostDescription;
59import org.onosproject.net.host.HostProvider;
60import org.onosproject.net.host.HostProviderRegistry;
61import org.onosproject.net.host.HostProviderService;
62import org.onosproject.net.packet.DefaultOutboundPacket;
63import org.onosproject.net.packet.PacketContext;
64import org.onosproject.net.packet.PacketPriority;
65import org.onosproject.net.packet.PacketProcessor;
66import org.onosproject.net.packet.PacketService;
67import org.onosproject.net.provider.AbstractProvider;
68import org.onosproject.net.provider.ProviderId;
69import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71
72import java.nio.ByteBuffer;
73import java.util.ArrayList;
samanwita pal2a313402015-09-14 16:03:22 -070074import java.util.Date;
samanwita palf28207b2015-09-04 10:41:56 -070075import java.util.HashSet;
76import java.util.List;
77import java.util.Map;
samanwita pal7ccc2bc2015-09-14 19:53:15 -070078import java.util.Objects;
samanwita palf28207b2015-09-04 10:41:56 -070079import java.util.Set;
samanwita pal2a313402015-09-14 16:03:22 -070080import java.util.concurrent.TimeUnit;
samanwita palf28207b2015-09-04 10:41:56 -070081import static org.onlab.packet.MacAddress.valueOf;
82import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
83
84/**
85 * Skeletal ONOS DHCP Server application.
86 */
87@Component(immediate = true)
88@Service
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070089public class DhcpManager implements DhcpService {
samanwita palf28207b2015-09-04 10:41:56 -070090
91 private static final ProviderId PID = new ProviderId("of", "org.onosproject.dhcp", true);
92 private final Logger log = LoggerFactory.getLogger(getClass());
93
Thomas Vachuska00090442015-09-11 18:08:04 -070094 private final InternalConfigListener cfgListener = new InternalConfigListener();
samanwita palf28207b2015-09-04 10:41:56 -070095
96 private final Set<ConfigFactory> factories = ImmutableSet.of(
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070097 new ConfigFactory<ApplicationId, DhcpConfig>(APP_SUBJECT_FACTORY,
98 DhcpConfig.class,
samanwita palf28207b2015-09-04 10:41:56 -070099 "dhcp") {
100 @Override
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700101 public DhcpConfig createConfig() {
102 return new DhcpConfig();
samanwita palf28207b2015-09-04 10:41:56 -0700103 }
samanwita palf28207b2015-09-04 10:41:56 -0700104 }
105 );
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected NetworkConfigRegistry cfgService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected PacketService packetService;
111
Jonathan Hartb35540a2015-11-17 09:30:56 -0800112 private DhcpPacketProcessor processor = new DhcpPacketProcessor();
samanwita palf28207b2015-09-04 10:41:56 -0700113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected CoreService coreService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700118 protected DhcpStore dhcpStore;
samanwita palf28207b2015-09-04 10:41:56 -0700119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected HostProviderRegistry hostProviderRegistry;
122
123 protected HostProviderService hostProviderService;
124
samanwita pal2a313402015-09-14 16:03:22 -0700125 private final HostProvider hostProvider = new InternalHostProvider();
126
samanwita palf28207b2015-09-04 10:41:56 -0700127 private ApplicationId appId;
128
129 // Hardcoded values are default values.
130
samanwita pal2a313402015-09-14 16:03:22 -0700131 private static Ip4Address myIP = Ip4Address.valueOf("10.0.0.2");
samanwita palf28207b2015-09-04 10:41:56 -0700132
133 private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
134
135 /**
136 * leaseTime - 10 mins or 600s.
137 * renewalTime - 5 mins or 300s.
138 * rebindingTime - 6 mins or 360s.
139 */
140
141 private static int leaseTime = 600;
142
143 private static int renewalTime = 300;
144
145 private static int rebindingTime = 360;
146
147 private static byte packetTTL = (byte) 127;
148
samanwita pal2a313402015-09-14 16:03:22 -0700149 private static Ip4Address subnetMask = Ip4Address.valueOf("255.0.0.0");
samanwita palf28207b2015-09-04 10:41:56 -0700150
samanwita pal2a313402015-09-14 16:03:22 -0700151 private static Ip4Address broadcastAddress = Ip4Address.valueOf("10.255.255.255");
samanwita palf28207b2015-09-04 10:41:56 -0700152
samanwita pal2a313402015-09-14 16:03:22 -0700153 private static Ip4Address routerAddress = Ip4Address.valueOf("10.0.0.2");
samanwita palf28207b2015-09-04 10:41:56 -0700154
samanwita pal2a313402015-09-14 16:03:22 -0700155 private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
156
samanwita pala3d1d1c2015-09-25 11:50:15 -0700157 private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
158
samanwita pal2a313402015-09-14 16:03:22 -0700159 protected Timeout timeout;
160
161 protected static int timerDelay = 2;
samanwita palf28207b2015-09-04 10:41:56 -0700162
163 @Activate
164 protected void activate() {
165 // start the dhcp server
166 appId = coreService.registerApplication("org.onosproject.dhcp");
167
168 cfgService.addListener(cfgListener);
169 factories.forEach(cfgService::registerConfigFactory);
Thomas Vachuska00090442015-09-11 18:08:04 -0700170 cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class));
samanwita palf28207b2015-09-04 10:41:56 -0700171 hostProviderService = hostProviderRegistry.register(hostProvider);
Thomas Vachuska2a645d42015-09-11 18:58:36 -0700172 packetService.addProcessor(processor, PacketProcessor.director(0));
samanwita palf28207b2015-09-04 10:41:56 -0700173 requestPackets();
samanwita pal2a313402015-09-14 16:03:22 -0700174 timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita palf28207b2015-09-04 10:41:56 -0700175 log.info("Started");
176 }
177
178 @Deactivate
179 protected void deactivate() {
180 cfgService.removeListener(cfgListener);
181 factories.forEach(cfgService::unregisterConfigFactory);
182 packetService.removeProcessor(processor);
183 hostProviderRegistry.unregister(hostProvider);
184 hostProviderService = null;
185 cancelPackets();
samanwita pal2a313402015-09-14 16:03:22 -0700186 timeout.cancel();
samanwita palf28207b2015-09-04 10:41:56 -0700187 log.info("Stopped");
188 }
189
190 /**
191 * Request packet in via PacketService.
192 */
193 private void requestPackets() {
194
195 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
196 .matchEthType(Ethernet.TYPE_IPV4)
197 .matchIPProtocol(IPv4.PROTOCOL_UDP)
198 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
199 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
200 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
201
202 selectorServer = DefaultTrafficSelector.builder()
203 .matchEthType(Ethernet.TYPE_ARP);
204 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
205 }
206
207 /**
208 * Cancel requested packets in via packet service.
209 */
210 private void cancelPackets() {
211 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
212 .matchEthType(Ethernet.TYPE_IPV4)
213 .matchIPProtocol(IPv4.PROTOCOL_UDP)
214 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
215 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
216 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
217
218 selectorServer = DefaultTrafficSelector.builder()
219 .matchEthType(Ethernet.TYPE_ARP);
220 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
221 }
222
223 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700224 public Map<HostId, IpAssignment> listMapping() {
samanwita pal0bff4302015-09-15 13:37:00 -0700225 return dhcpStore.listAssignedMapping();
samanwita palf28207b2015-09-04 10:41:56 -0700226 }
227
228 @Override
229 public int getLeaseTime() {
230 return leaseTime;
231 }
232
233 @Override
234 public int getRenewalTime() {
235 return renewalTime;
236 }
237
238 @Override
239 public int getRebindingTime() {
240 return rebindingTime;
241 }
242
243 @Override
daniel877bb2f2015-11-12 21:33:05 +0900244 public boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress, boolean rangeNotEnforced,
danielcd9deed2015-10-30 17:16:16 +0900245 List<Ip4Address> addressList) {
246 log.debug("setStaticMapping is called with Mac: {}, Ip: {} addressList: {}",
247 macID.toString(), ipAddress.toString(), addressList.toString());
248
daniel877bb2f2015-11-12 21:33:05 +0900249 return dhcpStore.assignStaticIP(macID, ipAddress, rangeNotEnforced, addressList);
samanwita palf28207b2015-09-04 10:41:56 -0700250 }
251
252 @Override
253 public boolean removeStaticMapping(MacAddress macID) {
254 return dhcpStore.removeStaticIP(macID);
255 }
256
257 @Override
258 public Iterable<Ip4Address> getAvailableIPs() {
259 return dhcpStore.getAvailableIPs();
260 }
261
Jonathan Hartb35540a2015-11-17 09:30:56 -0800262 private class DhcpPacketProcessor implements PacketProcessor {
samanwita palf28207b2015-09-04 10:41:56 -0700263
264 /**
265 * Builds the DHCP Reply packet.
266 *
267 * @param packet the incoming Ethernet frame
268 * @param ipOffered the IP offered by the DHCP Server
269 * @param outgoingMessageType the message type of the outgoing packet
270 * @return the Ethernet reply frame
271 */
samanwita pal2a313402015-09-14 16:03:22 -0700272 private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
samanwita palf28207b2015-09-04 10:41:56 -0700273
danielcd9deed2015-10-30 17:16:16 +0900274 Ip4Address subnetMaskReply;
275 Ip4Address dhcpServerReply;
276 Ip4Address routerAddressReply;
277 Ip4Address domainServerReply;
278 IpAssignment ipAssignment;
279
280 ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(HostId.hostId(packet.getSourceMAC()));
281
daniel877bb2f2015-11-12 21:33:05 +0900282 if (ipAssignment != null && ipAssignment.rangeNotEnforced()) {
danielcd9deed2015-10-30 17:16:16 +0900283 subnetMaskReply = ipAssignment.subnetMask();
284 dhcpServerReply = ipAssignment.dhcpServer();
285 domainServerReply = ipAssignment.domainServer();
286 routerAddressReply = ipAssignment.routerAddress();
287 } else {
288 subnetMaskReply = subnetMask;
289 dhcpServerReply = myIP;
290 routerAddressReply = routerAddress;
291 domainServerReply = domainServer;
292 }
293
samanwita palf28207b2015-09-04 10:41:56 -0700294 // Ethernet Frame.
295 Ethernet ethReply = new Ethernet();
296 ethReply.setSourceMACAddress(myMAC);
297 ethReply.setDestinationMACAddress(packet.getSourceMAC());
298 ethReply.setEtherType(Ethernet.TYPE_IPV4);
299 ethReply.setVlanID(packet.getVlanID());
300
301 // IP Packet
302 IPv4 ipv4Packet = (IPv4) packet.getPayload();
303 IPv4 ipv4Reply = new IPv4();
danielcd9deed2015-10-30 17:16:16 +0900304 ipv4Reply.setSourceAddress(dhcpServerReply.toInt());
samanwita pal2a313402015-09-14 16:03:22 -0700305 ipv4Reply.setDestinationAddress(ipOffered.toInt());
samanwita palf28207b2015-09-04 10:41:56 -0700306 ipv4Reply.setTtl(packetTTL);
307
308 // UDP Datagram.
309 UDP udpPacket = (UDP) ipv4Packet.getPayload();
310 UDP udpReply = new UDP();
311 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
312 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
313
314 // DHCP Payload.
315 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
316 DHCP dhcpReply = new DHCP();
317 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700318 dhcpReply.setFlags(dhcpPacket.getFlags());
319 dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
samanwita palf28207b2015-09-04 10:41:56 -0700320 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700321 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
322
323 if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
324 dhcpReply.setYourIPAddress(ipOffered.toInt());
danielcd9deed2015-10-30 17:16:16 +0900325 dhcpReply.setServerIPAddress(dhcpServerReply.toInt());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700326 if (dhcpPacket.getGatewayIPAddress() == 0) {
327 ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
328 }
329 }
samanwita palf28207b2015-09-04 10:41:56 -0700330 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
331 dhcpReply.setHardwareAddressLength((byte) 6);
332
333 // DHCP Options.
334 DHCPOption option = new DHCPOption();
335 List<DHCPOption> optionList = new ArrayList<>();
336
337 // DHCP Message Type.
338 option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
339 option.setLength((byte) 1);
340 byte[] optionData = {outgoingMessageType};
341 option.setData(optionData);
342 optionList.add(option);
343
344 // DHCP Server Identifier.
345 option = new DHCPOption();
346 option.setCode(DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue());
347 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900348 option.setData(dhcpServerReply.toOctets());
samanwita palf28207b2015-09-04 10:41:56 -0700349 optionList.add(option);
350
samanwita pala3d1d1c2015-09-25 11:50:15 -0700351 if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
samanwita palf28207b2015-09-04 10:41:56 -0700352
samanwita pala3d1d1c2015-09-25 11:50:15 -0700353 // IP Address Lease Time.
354 option = new DHCPOption();
355 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
356 option.setLength((byte) 4);
357 option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
358 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700359
samanwita pala3d1d1c2015-09-25 11:50:15 -0700360 // IP Address Renewal Time.
361 option = new DHCPOption();
362 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
363 option.setLength((byte) 4);
364 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
365 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700366
samanwita pala3d1d1c2015-09-25 11:50:15 -0700367 // IP Address Rebinding Time.
368 option = new DHCPOption();
369 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
370 option.setLength((byte) 4);
371 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
372 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700373
samanwita pala3d1d1c2015-09-25 11:50:15 -0700374 // Subnet Mask.
375 option = new DHCPOption();
376 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
377 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900378 option.setData(subnetMaskReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700379 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700380
samanwita pala3d1d1c2015-09-25 11:50:15 -0700381 // Broadcast Address.
382 option = new DHCPOption();
383 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
384 option.setLength((byte) 4);
385 option.setData(broadcastAddress.toOctets());
386 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700387
samanwita pala3d1d1c2015-09-25 11:50:15 -0700388 // Router Address.
389 option = new DHCPOption();
390 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
391 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900392 option.setData(routerAddressReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700393 optionList.add(option);
394
395 // DNS Server Address.
396 option = new DHCPOption();
397 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
398 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900399 option.setData(domainServerReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700400 optionList.add(option);
401 }
samanwita palf28207b2015-09-04 10:41:56 -0700402
403 // End Option.
404 option = new DHCPOption();
405 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
406 option.setLength((byte) 1);
407 optionList.add(option);
408
409 dhcpReply.setOptions(optionList);
samanwita palf28207b2015-09-04 10:41:56 -0700410 udpReply.setPayload(dhcpReply);
411 ipv4Reply.setPayload(udpReply);
412 ethReply.setPayload(ipv4Reply);
413
414 return ethReply;
415 }
416
417 /**
418 * Sends the Ethernet reply frame via the Packet Service.
419 *
420 * @param context the context of the incoming frame
421 * @param reply the Ethernet reply frame
422 */
423 private void sendReply(PacketContext context, Ethernet reply) {
424 if (reply != null) {
425 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
426 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
427 builder.setOutput(sourcePoint.port());
Thomas Vachuska00090442015-09-11 18:08:04 -0700428 context.block();
samanwita palf28207b2015-09-04 10:41:56 -0700429 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
samanwita pal8969cbe2015-09-04 13:31:30 -0700430 builder.build(), ByteBuffer.wrap(reply.serialize())));
samanwita palf28207b2015-09-04 10:41:56 -0700431 }
432 }
433
434 /**
435 * Processes the DHCP Payload and initiates a reply to the client.
436 *
437 * @param context context of the incoming message
438 * @param dhcpPayload the extracted DHCP payload
439 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800440 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
samanwita palf28207b2015-09-04 10:41:56 -0700441 Ethernet packet = context.inPacket().parsed();
442 boolean flagIfRequestedIP = false;
443 boolean flagIfServerIP = false;
444 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
445 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
446
447 if (dhcpPayload != null) {
448
samanwita pal2a313402015-09-14 16:03:22 -0700449 DHCPPacketType incomingPacketType = DHCPPacketType.getType(0);
samanwita palf28207b2015-09-04 10:41:56 -0700450 for (DHCPOption option : dhcpPayload.getOptions()) {
451 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) {
452 byte[] data = option.getData();
samanwita pal2a313402015-09-14 16:03:22 -0700453 incomingPacketType = DHCPPacketType.getType(data[0]);
samanwita palf28207b2015-09-04 10:41:56 -0700454 }
455 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue()) {
456 byte[] data = option.getData();
457 requestedIP = Ip4Address.valueOf(data);
458 flagIfRequestedIP = true;
459 }
460 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue()) {
461 byte[] data = option.getData();
462 serverIP = Ip4Address.valueOf(data);
463 flagIfServerIP = true;
464 }
465 }
samanwita pal8969cbe2015-09-04 13:31:30 -0700466 DHCPPacketType outgoingPacketType;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800467 MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
samanwita pal2a313402015-09-14 16:03:22 -0700468 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
Jonathan Hartb35540a2015-11-17 09:30:56 -0800469 HostId hostId = HostId.hostId(clientMac, vlanId);
samanwita palf28207b2015-09-04 10:41:56 -0700470
samanwita pal2a313402015-09-14 16:03:22 -0700471 if (incomingPacketType.getValue() == DHCPPacketType.DHCPDISCOVER.getValue()) {
samanwita palf28207b2015-09-04 10:41:56 -0700472
samanwita pal8969cbe2015-09-04 13:31:30 -0700473 outgoingPacketType = DHCPPacketType.DHCPOFFER;
danielcd9deed2015-10-30 17:16:16 +0900474 Ip4Address ipOffered = null;
475 ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
476
samanwita pal2a313402015-09-14 16:03:22 -0700477 if (ipOffered != null) {
478 Ethernet ethReply = buildReply(packet, ipOffered,
479 (byte) outgoingPacketType.getValue());
480 sendReply(context, ethReply);
481 }
samanwita pal2a313402015-09-14 16:03:22 -0700482 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) {
samanwita palf28207b2015-09-04 10:41:56 -0700483
samanwita palf28207b2015-09-04 10:41:56 -0700484 if (flagIfServerIP && flagIfRequestedIP) {
485 // SELECTING state
samanwita palf28207b2015-09-04 10:41:56 -0700486
daniel877bb2f2015-11-12 21:33:05 +0900487
Jonathan Hartb35540a2015-11-17 09:30:56 -0800488 if (dhcpStore.getIpAssignmentFromAllocationMap(HostId.hostId(clientMac))
daniel877bb2f2015-11-12 21:33:05 +0900489 .rangeNotEnforced()) {
danielcd9deed2015-10-30 17:16:16 +0900490 outgoingPacketType = DHCPPacketType.DHCPACK;
samanwita pala3d1d1c2015-09-25 11:50:15 -0700491 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700492 sendReply(context, ethReply);
danielcd9deed2015-10-30 17:16:16 +0900493 } else {
494 if (myIP.equals(serverIP)) {
495 if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
496 outgoingPacketType = DHCPPacketType.DHCPACK;
497 discoverHost(context, requestedIP);
498 } else {
499 outgoingPacketType = DHCPPacketType.DHCPNAK;
500 }
501 Ethernet ethReply = buildReply(packet, requestedIP,
502 (byte) outgoingPacketType.getValue());
503 sendReply(context, ethReply);
504 }
samanwita palf28207b2015-09-04 10:41:56 -0700505 }
506 } else if (flagIfRequestedIP) {
507 // INIT-REBOOT state
danielcd9deed2015-10-30 17:16:16 +0900508 if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700509 outgoingPacketType = DHCPPacketType.DHCPACK;
510 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700511 sendReply(context, ethReply);
512 discoverHost(context, requestedIP);
513 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700514
samanwita palf28207b2015-09-04 10:41:56 -0700515 } else {
516 // RENEWING and REBINDING state
517 int ciaadr = dhcpPayload.getClientIPAddress();
518 if (ciaadr != 0) {
519 Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
danielcd9deed2015-10-30 17:16:16 +0900520 if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime, false, Lists.newArrayList())) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700521 outgoingPacketType = DHCPPacketType.DHCPACK;
samanwita palf28207b2015-09-04 10:41:56 -0700522 discoverHost(context, clientIaddr);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700523 } else if (packet.getEtherType() == Ethernet.TYPE_IPV4 &&
524 ((IPv4) packet.getPayload()).getDestinationAddress() == myIP.toInt()) {
525 outgoingPacketType = DHCPPacketType.DHCPNAK;
526 } else {
527 return;
samanwita palf28207b2015-09-04 10:41:56 -0700528 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700529 Ethernet ethReply = buildReply(packet, clientIaddr, (byte) outgoingPacketType.getValue());
530 sendReply(context, ethReply);
samanwita palf28207b2015-09-04 10:41:56 -0700531 }
532 }
samanwita pal2a313402015-09-14 16:03:22 -0700533 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) {
samanwita palc40e5ed2015-09-24 11:01:51 -0700534 Ip4Address ip4Address = dhcpStore.releaseIP(hostId);
535 if (ip4Address != null) {
536 hostProviderService.removeIpFromHost(hostId, ip4Address);
537 }
samanwita palf28207b2015-09-04 10:41:56 -0700538 }
539 }
540 }
541
542 /**
543 * Processes the ARP Payload and initiates a reply to the client.
544 *
545 * @param context context of the incoming message
546 * @param packet the ethernet payload
547 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800548 private void processArpPacket(PacketContext context, Ethernet packet) {
samanwita palf28207b2015-09-04 10:41:56 -0700549
550 ARP arpPacket = (ARP) packet.getPayload();
551
552 ARP arpReply = (ARP) arpPacket.clone();
553 arpReply.setOpCode(ARP.OP_REPLY);
554
555 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
556 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
557 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
558 arpReply.setSenderHardwareAddress(myMAC.toBytes());
559
560 // Ethernet Frame.
561 Ethernet ethReply = new Ethernet();
562 ethReply.setSourceMACAddress(myMAC);
563 ethReply.setDestinationMACAddress(packet.getSourceMAC());
564 ethReply.setEtherType(Ethernet.TYPE_ARP);
565 ethReply.setVlanID(packet.getVlanID());
566
567 ethReply.setPayload(arpReply);
568 sendReply(context, ethReply);
569 }
570
571 /**
572 * Integrates hosts learned through DHCP into topology.
573 * @param context context of the incoming message
574 * @param ipAssigned IP Address assigned to the host by DHCP Manager
575 */
576 private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
577 Ethernet packet = context.inPacket().parsed();
578 MacAddress mac = packet.getSourceMAC();
579 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
580 HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0);
581
582 Set<IpAddress> ips = new HashSet<>();
583 ips.add(ipAssigned);
584
585 HostId hostId = HostId.hostId(mac, vlanId);
586 DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
587 hostProviderService.hostDetected(hostId, desc);
588 }
589
590
591 @Override
592 public void process(PacketContext context) {
samanwita palf28207b2015-09-04 10:41:56 -0700593 Ethernet packet = context.inPacket().parsed();
594 if (packet == null) {
595 return;
596 }
597
598 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
599 IPv4 ipv4Packet = (IPv4) packet.getPayload();
600
601 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
602 UDP udpPacket = (UDP) ipv4Packet.getPayload();
603
604 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
605 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
606 // This is meant for the dhcp server so process the packet here.
607
608 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Jonathan Hartb35540a2015-11-17 09:30:56 -0800609 processDhcpPacket(context, dhcpPayload);
samanwita palf28207b2015-09-04 10:41:56 -0700610 }
611 }
612 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
613 ARP arpPacket = (ARP) packet.getPayload();
614
615 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700616 Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
samanwita palf28207b2015-09-04 10:41:56 -0700617
Jonathan Hartb35540a2015-11-17 09:30:56 -0800618 processArpPacket(context, packet);
samanwita palf28207b2015-09-04 10:41:56 -0700619
620 }
621 }
622 }
623 }
624
625 private class InternalConfigListener implements NetworkConfigListener {
626
627 /**
628 * Reconfigures the DHCP Server according to the configuration parameters passed.
629 *
630 * @param cfg configuration object
631 */
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700632 private void reconfigureNetwork(DhcpConfig cfg) {
Thomas Vachuska00090442015-09-11 18:08:04 -0700633 if (cfg == null) {
634 return;
635 }
samanwita palf28207b2015-09-04 10:41:56 -0700636 if (cfg.ip() != null) {
637 myIP = cfg.ip();
638 }
639 if (cfg.mac() != null) {
samanwita pal2a313402015-09-14 16:03:22 -0700640 myMAC = cfg.mac();
samanwita palf28207b2015-09-04 10:41:56 -0700641 }
642 if (cfg.subnetMask() != null) {
643 subnetMask = cfg.subnetMask();
644 }
645 if (cfg.broadcastAddress() != null) {
646 broadcastAddress = cfg.broadcastAddress();
647 }
648 if (cfg.routerAddress() != null) {
649 routerAddress = cfg.routerAddress();
650 }
651 if (cfg.domainServer() != null) {
652 domainServer = cfg.domainServer();
653 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700654 if (cfg.ttl() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700655 packetTTL = (byte) cfg.ttl();
samanwita palf28207b2015-09-04 10:41:56 -0700656 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700657 if (cfg.leaseTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700658 leaseTime = cfg.leaseTime();
samanwita palf28207b2015-09-04 10:41:56 -0700659 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700660 if (cfg.renewTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700661 renewalTime = cfg.renewTime();
samanwita palf28207b2015-09-04 10:41:56 -0700662 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700663 if (cfg.rebindTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700664 rebindingTime = cfg.rebindTime();
665 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700666 if (cfg.defaultTimeout() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700667 dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout());
668 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700669 if (cfg.timerDelay() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700670 timerDelay = cfg.timerDelay();
671 }
672 if ((cfg.startIp() != null) && (cfg.endIp() != null)) {
673 dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp());
samanwita palf28207b2015-09-04 10:41:56 -0700674 }
675 }
676
samanwita palf28207b2015-09-04 10:41:56 -0700677
678 @Override
679 public void event(NetworkConfigEvent event) {
680
681 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
samanwita pal2a313402015-09-14 16:03:22 -0700682 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
683 event.configClass().equals(DhcpConfig.class)) {
684
685 DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class);
686 reconfigureNetwork(cfg);
687 log.info("Reconfigured");
samanwita palf28207b2015-09-04 10:41:56 -0700688 }
689 }
690 }
691
692 private class InternalHostProvider extends AbstractProvider implements HostProvider {
693
694 /**
695 * Creates a provider with the supplier identifier.
696 */
697 protected InternalHostProvider() {
698 super(PID);
699 }
700
701 @Override
702 public void triggerProbe(Host host) {
703 // nothing to do
704 }
705 }
samanwita pal2a313402015-09-14 16:03:22 -0700706
707 private class PurgeListTask implements TimerTask {
708
709 @Override
710 public void run(Timeout to) {
711 IpAssignment ipAssignment;
712 Date dateNow = new Date();
713
samanwita pal0bff4302015-09-15 13:37:00 -0700714 Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping();
samanwita pal2a313402015-09-14 16:03:22 -0700715 for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) {
716 ipAssignment = entry.getValue();
717
718 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
719 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
720 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
721
samanwita palc40e5ed2015-09-24 11:01:51 -0700722 Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
723 if (ip4Address != null) {
724 hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
725 }
samanwita pal2a313402015-09-14 16:03:22 -0700726 }
727 }
728 timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
729 }
730 }
Jonathan Hartb35540a2015-11-17 09:30:56 -0800731}