blob: 3c6573ad3e081c24fd4bdea0b71e7b5e5a582679 [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);
daniel02f3af02015-12-17 18:44:52 +0900357 option.setData(ByteBuffer.allocate(4)
358 .putInt(ipAssignment == null ? leaseTime : ipAssignment.leasePeriod()).array());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700359 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700360
samanwita pala3d1d1c2015-09-25 11:50:15 -0700361 // IP Address Renewal Time.
362 option = new DHCPOption();
363 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
364 option.setLength((byte) 4);
365 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
366 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700367
samanwita pala3d1d1c2015-09-25 11:50:15 -0700368 // IP Address Rebinding Time.
369 option = new DHCPOption();
370 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
371 option.setLength((byte) 4);
372 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
373 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700374
samanwita pala3d1d1c2015-09-25 11:50:15 -0700375 // Subnet Mask.
376 option = new DHCPOption();
377 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
378 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900379 option.setData(subnetMaskReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700380 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700381
samanwita pala3d1d1c2015-09-25 11:50:15 -0700382 // Broadcast Address.
383 option = new DHCPOption();
384 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
385 option.setLength((byte) 4);
386 option.setData(broadcastAddress.toOctets());
387 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700388
samanwita pala3d1d1c2015-09-25 11:50:15 -0700389 // Router Address.
390 option = new DHCPOption();
391 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
392 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900393 option.setData(routerAddressReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700394 optionList.add(option);
395
396 // DNS Server Address.
397 option = new DHCPOption();
398 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
399 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900400 option.setData(domainServerReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700401 optionList.add(option);
402 }
samanwita palf28207b2015-09-04 10:41:56 -0700403
404 // End Option.
405 option = new DHCPOption();
406 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
407 option.setLength((byte) 1);
408 optionList.add(option);
409
410 dhcpReply.setOptions(optionList);
samanwita palf28207b2015-09-04 10:41:56 -0700411 udpReply.setPayload(dhcpReply);
412 ipv4Reply.setPayload(udpReply);
413 ethReply.setPayload(ipv4Reply);
414
415 return ethReply;
416 }
417
418 /**
419 * Sends the Ethernet reply frame via the Packet Service.
420 *
421 * @param context the context of the incoming frame
422 * @param reply the Ethernet reply frame
423 */
424 private void sendReply(PacketContext context, Ethernet reply) {
425 if (reply != null) {
426 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
427 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
428 builder.setOutput(sourcePoint.port());
Thomas Vachuska00090442015-09-11 18:08:04 -0700429 context.block();
samanwita palf28207b2015-09-04 10:41:56 -0700430 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
samanwita pal8969cbe2015-09-04 13:31:30 -0700431 builder.build(), ByteBuffer.wrap(reply.serialize())));
samanwita palf28207b2015-09-04 10:41:56 -0700432 }
433 }
434
435 /**
436 * Processes the DHCP Payload and initiates a reply to the client.
437 *
438 * @param context context of the incoming message
439 * @param dhcpPayload the extracted DHCP payload
440 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800441 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
samanwita palf28207b2015-09-04 10:41:56 -0700442 Ethernet packet = context.inPacket().parsed();
443 boolean flagIfRequestedIP = false;
444 boolean flagIfServerIP = false;
445 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
446 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
447
448 if (dhcpPayload != null) {
449
samanwita pal2a313402015-09-14 16:03:22 -0700450 DHCPPacketType incomingPacketType = DHCPPacketType.getType(0);
samanwita palf28207b2015-09-04 10:41:56 -0700451 for (DHCPOption option : dhcpPayload.getOptions()) {
452 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) {
453 byte[] data = option.getData();
samanwita pal2a313402015-09-14 16:03:22 -0700454 incomingPacketType = DHCPPacketType.getType(data[0]);
samanwita palf28207b2015-09-04 10:41:56 -0700455 }
456 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue()) {
457 byte[] data = option.getData();
458 requestedIP = Ip4Address.valueOf(data);
459 flagIfRequestedIP = true;
460 }
461 if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue()) {
462 byte[] data = option.getData();
463 serverIP = Ip4Address.valueOf(data);
464 flagIfServerIP = true;
465 }
466 }
samanwita pal8969cbe2015-09-04 13:31:30 -0700467 DHCPPacketType outgoingPacketType;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800468 MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
samanwita pal2a313402015-09-14 16:03:22 -0700469 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
Jonathan Hartb35540a2015-11-17 09:30:56 -0800470 HostId hostId = HostId.hostId(clientMac, vlanId);
samanwita palf28207b2015-09-04 10:41:56 -0700471
samanwita pal2a313402015-09-14 16:03:22 -0700472 if (incomingPacketType.getValue() == DHCPPacketType.DHCPDISCOVER.getValue()) {
samanwita palf28207b2015-09-04 10:41:56 -0700473
samanwita pal8969cbe2015-09-04 13:31:30 -0700474 outgoingPacketType = DHCPPacketType.DHCPOFFER;
danielcd9deed2015-10-30 17:16:16 +0900475 Ip4Address ipOffered = null;
476 ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
477
samanwita pal2a313402015-09-14 16:03:22 -0700478 if (ipOffered != null) {
479 Ethernet ethReply = buildReply(packet, ipOffered,
480 (byte) outgoingPacketType.getValue());
481 sendReply(context, ethReply);
482 }
samanwita pal2a313402015-09-14 16:03:22 -0700483 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) {
samanwita palf28207b2015-09-04 10:41:56 -0700484
samanwita palf28207b2015-09-04 10:41:56 -0700485 if (flagIfServerIP && flagIfRequestedIP) {
486 // SELECTING state
samanwita palf28207b2015-09-04 10:41:56 -0700487
daniel877bb2f2015-11-12 21:33:05 +0900488
Jonathan Hartb35540a2015-11-17 09:30:56 -0800489 if (dhcpStore.getIpAssignmentFromAllocationMap(HostId.hostId(clientMac))
daniel877bb2f2015-11-12 21:33:05 +0900490 .rangeNotEnforced()) {
danielcd9deed2015-10-30 17:16:16 +0900491 outgoingPacketType = DHCPPacketType.DHCPACK;
samanwita pala3d1d1c2015-09-25 11:50:15 -0700492 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700493 sendReply(context, ethReply);
danielcd9deed2015-10-30 17:16:16 +0900494 } else {
495 if (myIP.equals(serverIP)) {
496 if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
497 outgoingPacketType = DHCPPacketType.DHCPACK;
498 discoverHost(context, requestedIP);
499 } else {
500 outgoingPacketType = DHCPPacketType.DHCPNAK;
501 }
502 Ethernet ethReply = buildReply(packet, requestedIP,
503 (byte) outgoingPacketType.getValue());
504 sendReply(context, ethReply);
505 }
samanwita palf28207b2015-09-04 10:41:56 -0700506 }
507 } else if (flagIfRequestedIP) {
508 // INIT-REBOOT state
danielcd9deed2015-10-30 17:16:16 +0900509 if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700510 outgoingPacketType = DHCPPacketType.DHCPACK;
511 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700512 sendReply(context, ethReply);
513 discoverHost(context, requestedIP);
514 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700515
samanwita palf28207b2015-09-04 10:41:56 -0700516 } else {
517 // RENEWING and REBINDING state
518 int ciaadr = dhcpPayload.getClientIPAddress();
519 if (ciaadr != 0) {
520 Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
danielcd9deed2015-10-30 17:16:16 +0900521 if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime, false, Lists.newArrayList())) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700522 outgoingPacketType = DHCPPacketType.DHCPACK;
samanwita palf28207b2015-09-04 10:41:56 -0700523 discoverHost(context, clientIaddr);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700524 } else if (packet.getEtherType() == Ethernet.TYPE_IPV4 &&
525 ((IPv4) packet.getPayload()).getDestinationAddress() == myIP.toInt()) {
526 outgoingPacketType = DHCPPacketType.DHCPNAK;
527 } else {
528 return;
samanwita palf28207b2015-09-04 10:41:56 -0700529 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700530 Ethernet ethReply = buildReply(packet, clientIaddr, (byte) outgoingPacketType.getValue());
531 sendReply(context, ethReply);
samanwita palf28207b2015-09-04 10:41:56 -0700532 }
533 }
samanwita pal2a313402015-09-14 16:03:22 -0700534 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) {
samanwita palc40e5ed2015-09-24 11:01:51 -0700535 Ip4Address ip4Address = dhcpStore.releaseIP(hostId);
536 if (ip4Address != null) {
537 hostProviderService.removeIpFromHost(hostId, ip4Address);
538 }
samanwita palf28207b2015-09-04 10:41:56 -0700539 }
540 }
541 }
542
543 /**
544 * Processes the ARP Payload and initiates a reply to the client.
545 *
546 * @param context context of the incoming message
547 * @param packet the ethernet payload
548 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800549 private void processArpPacket(PacketContext context, Ethernet packet) {
samanwita palf28207b2015-09-04 10:41:56 -0700550
551 ARP arpPacket = (ARP) packet.getPayload();
552
553 ARP arpReply = (ARP) arpPacket.clone();
554 arpReply.setOpCode(ARP.OP_REPLY);
555
556 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
557 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
558 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
559 arpReply.setSenderHardwareAddress(myMAC.toBytes());
560
561 // Ethernet Frame.
562 Ethernet ethReply = new Ethernet();
563 ethReply.setSourceMACAddress(myMAC);
564 ethReply.setDestinationMACAddress(packet.getSourceMAC());
565 ethReply.setEtherType(Ethernet.TYPE_ARP);
566 ethReply.setVlanID(packet.getVlanID());
567
568 ethReply.setPayload(arpReply);
569 sendReply(context, ethReply);
570 }
571
572 /**
573 * Integrates hosts learned through DHCP into topology.
574 * @param context context of the incoming message
575 * @param ipAssigned IP Address assigned to the host by DHCP Manager
576 */
577 private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
578 Ethernet packet = context.inPacket().parsed();
579 MacAddress mac = packet.getSourceMAC();
580 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
581 HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0);
582
583 Set<IpAddress> ips = new HashSet<>();
584 ips.add(ipAssigned);
585
586 HostId hostId = HostId.hostId(mac, vlanId);
587 DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
Ray Milkeydc083442016-02-22 11:27:57 -0800588 hostProviderService.hostDetected(hostId, desc, false);
samanwita palf28207b2015-09-04 10:41:56 -0700589 }
590
591
592 @Override
593 public void process(PacketContext context) {
samanwita palf28207b2015-09-04 10:41:56 -0700594 Ethernet packet = context.inPacket().parsed();
595 if (packet == null) {
596 return;
597 }
598
599 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
600 IPv4 ipv4Packet = (IPv4) packet.getPayload();
601
602 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
603 UDP udpPacket = (UDP) ipv4Packet.getPayload();
604
605 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
606 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
607 // This is meant for the dhcp server so process the packet here.
608
609 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Jonathan Hartb35540a2015-11-17 09:30:56 -0800610 processDhcpPacket(context, dhcpPayload);
samanwita palf28207b2015-09-04 10:41:56 -0700611 }
612 }
613 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
614 ARP arpPacket = (ARP) packet.getPayload();
615
616 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700617 Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
samanwita palf28207b2015-09-04 10:41:56 -0700618
Jonathan Hartb35540a2015-11-17 09:30:56 -0800619 processArpPacket(context, packet);
samanwita palf28207b2015-09-04 10:41:56 -0700620
621 }
622 }
623 }
624 }
625
626 private class InternalConfigListener implements NetworkConfigListener {
627
628 /**
629 * Reconfigures the DHCP Server according to the configuration parameters passed.
630 *
631 * @param cfg configuration object
632 */
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700633 private void reconfigureNetwork(DhcpConfig cfg) {
Thomas Vachuska00090442015-09-11 18:08:04 -0700634 if (cfg == null) {
635 return;
636 }
samanwita palf28207b2015-09-04 10:41:56 -0700637 if (cfg.ip() != null) {
638 myIP = cfg.ip();
639 }
640 if (cfg.mac() != null) {
samanwita pal2a313402015-09-14 16:03:22 -0700641 myMAC = cfg.mac();
samanwita palf28207b2015-09-04 10:41:56 -0700642 }
643 if (cfg.subnetMask() != null) {
644 subnetMask = cfg.subnetMask();
645 }
646 if (cfg.broadcastAddress() != null) {
647 broadcastAddress = cfg.broadcastAddress();
648 }
649 if (cfg.routerAddress() != null) {
650 routerAddress = cfg.routerAddress();
651 }
652 if (cfg.domainServer() != null) {
653 domainServer = cfg.domainServer();
654 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700655 if (cfg.ttl() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700656 packetTTL = (byte) cfg.ttl();
samanwita palf28207b2015-09-04 10:41:56 -0700657 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700658 if (cfg.leaseTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700659 leaseTime = cfg.leaseTime();
samanwita palf28207b2015-09-04 10:41:56 -0700660 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700661 if (cfg.renewTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700662 renewalTime = cfg.renewTime();
samanwita palf28207b2015-09-04 10:41:56 -0700663 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700664 if (cfg.rebindTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700665 rebindingTime = cfg.rebindTime();
666 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700667 if (cfg.defaultTimeout() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700668 dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout());
669 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700670 if (cfg.timerDelay() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700671 timerDelay = cfg.timerDelay();
672 }
673 if ((cfg.startIp() != null) && (cfg.endIp() != null)) {
674 dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp());
samanwita palf28207b2015-09-04 10:41:56 -0700675 }
676 }
677
samanwita palf28207b2015-09-04 10:41:56 -0700678
679 @Override
680 public void event(NetworkConfigEvent event) {
681
682 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
samanwita pal2a313402015-09-14 16:03:22 -0700683 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
684 event.configClass().equals(DhcpConfig.class)) {
685
686 DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class);
687 reconfigureNetwork(cfg);
688 log.info("Reconfigured");
samanwita palf28207b2015-09-04 10:41:56 -0700689 }
690 }
691 }
692
693 private class InternalHostProvider extends AbstractProvider implements HostProvider {
694
695 /**
696 * Creates a provider with the supplier identifier.
697 */
698 protected InternalHostProvider() {
699 super(PID);
700 }
701
702 @Override
703 public void triggerProbe(Host host) {
704 // nothing to do
705 }
706 }
samanwita pal2a313402015-09-14 16:03:22 -0700707
708 private class PurgeListTask implements TimerTask {
709
710 @Override
711 public void run(Timeout to) {
712 IpAssignment ipAssignment;
713 Date dateNow = new Date();
714
samanwita pal0bff4302015-09-15 13:37:00 -0700715 Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping();
samanwita pal2a313402015-09-14 16:03:22 -0700716 for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) {
717 ipAssignment = entry.getValue();
718
719 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
720 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
721 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
722
samanwita palc40e5ed2015-09-24 11:01:51 -0700723 Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
724 if (ip4Address != null) {
725 hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
726 }
samanwita pal2a313402015-09-14 16:03:22 -0700727 }
728 }
729 timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
730 }
731 }
Jonathan Hartb35540a2015-11-17 09:30:56 -0800732}