blob: dd2183835b0e707540e2515b88c8d592e6925f60 [file] [log] [blame]
samanwita palf28207b2015-09-04 10:41:56 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070018import com.google.common.base.Strings;
samanwita palf28207b2015-09-04 10:41:56 -070019import com.google.common.collect.ImmutableSet;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070020
samanwita palf28207b2015-09-04 10:41:56 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
samanwita palf28207b2015-09-04 10:41:56 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.apache.felix.scr.annotations.Service;
29import org.onlab.packet.ARP;
30import org.onlab.packet.DHCP;
31import org.onlab.packet.DHCPOption;
samanwita pal8969cbe2015-09-04 13:31:30 -070032import org.onlab.packet.DHCPPacketType;
samanwita palf28207b2015-09-04 10:41:56 -070033import org.onlab.packet.Ethernet;
34import org.onlab.packet.IPv4;
35import org.onlab.packet.Ip4Address;
36import org.onlab.packet.IpAddress;
37import org.onlab.packet.MacAddress;
38import org.onlab.packet.TpPort;
39import org.onlab.packet.UDP;
40import org.onlab.packet.VlanId;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070041import org.onlab.util.SharedScheduledExecutors;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070042import org.onlab.util.Tools;
43import org.onosproject.cfg.ComponentConfigService;
samanwita palf28207b2015-09-04 10:41:56 -070044import org.onosproject.core.ApplicationId;
45import org.onosproject.core.CoreService;
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070046import org.onosproject.dhcp.DhcpService;
47import org.onosproject.dhcp.DhcpStore;
48import org.onosproject.dhcp.IpAssignment;
samanwita palf28207b2015-09-04 10:41:56 -070049import org.onosproject.net.ConnectPoint;
50import org.onosproject.net.Host;
51import org.onosproject.net.HostId;
52import org.onosproject.net.HostLocation;
samanwita pal2a313402015-09-14 16:03:22 -070053import org.onosproject.net.config.ConfigFactory;
54import org.onosproject.net.config.NetworkConfigEvent;
55import org.onosproject.net.config.NetworkConfigListener;
56import org.onosproject.net.config.NetworkConfigRegistry;
samanwita palf28207b2015-09-04 10:41:56 -070057import org.onosproject.net.flow.DefaultTrafficSelector;
58import org.onosproject.net.flow.DefaultTrafficTreatment;
59import org.onosproject.net.flow.TrafficSelector;
60import org.onosproject.net.flow.TrafficTreatment;
61import org.onosproject.net.host.DefaultHostDescription;
62import org.onosproject.net.host.HostProvider;
63import org.onosproject.net.host.HostProviderRegistry;
64import org.onosproject.net.host.HostProviderService;
65import org.onosproject.net.packet.DefaultOutboundPacket;
66import org.onosproject.net.packet.PacketContext;
67import org.onosproject.net.packet.PacketPriority;
68import org.onosproject.net.packet.PacketProcessor;
69import org.onosproject.net.packet.PacketService;
70import org.onosproject.net.provider.AbstractProvider;
71import org.onosproject.net.provider.ProviderId;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070072import org.osgi.service.component.ComponentContext;
samanwita palf28207b2015-09-04 10:41:56 -070073import org.slf4j.Logger;
74import org.slf4j.LoggerFactory;
75
76import java.nio.ByteBuffer;
77import java.util.ArrayList;
samanwita pal2a313402015-09-14 16:03:22 -070078import java.util.Date;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070079import java.util.Dictionary;
samanwita palf28207b2015-09-04 10:41:56 -070080import java.util.HashSet;
81import java.util.List;
82import java.util.Map;
samanwita pal7ccc2bc2015-09-14 19:53:15 -070083import java.util.Objects;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070084import java.util.Optional;
samanwita palf28207b2015-09-04 10:41:56 -070085import java.util.Set;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070086import java.util.concurrent.ScheduledFuture;
samanwita pal2a313402015-09-14 16:03:22 -070087import java.util.concurrent.TimeUnit;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070088
89import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
90import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
91import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RequestedIP;
92import static org.onlab.packet.DHCPPacketType.DHCPACK;
93import static org.onlab.packet.DHCPPacketType.DHCPNAK;
94import static org.onlab.packet.DHCPPacketType.DHCPOFFER;
samanwita palf28207b2015-09-04 10:41:56 -070095import static org.onlab.packet.MacAddress.valueOf;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070096import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
97import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Requested;
samanwita palf28207b2015-09-04 10:41:56 -070098import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
99
100/**
101 * Skeletal ONOS DHCP Server application.
102 */
103@Component(immediate = true)
104@Service
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700105public class DhcpManager implements DhcpService {
samanwita palf28207b2015-09-04 10:41:56 -0700106
107 private static final ProviderId PID = new ProviderId("of", "org.onosproject.dhcp", true);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700108 private static final String ALLOW_HOST_DISCOVERY = "allowHostDiscovery";
109 private static final boolean DEFAULT_ALLOW_HOST_DISCOVERY = false;
110
samanwita palf28207b2015-09-04 10:41:56 -0700111 private final Logger log = LoggerFactory.getLogger(getClass());
112
Thomas Vachuska00090442015-09-11 18:08:04 -0700113 private final InternalConfigListener cfgListener = new InternalConfigListener();
samanwita palf28207b2015-09-04 10:41:56 -0700114
115 private final Set<ConfigFactory> factories = ImmutableSet.of(
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700116 new ConfigFactory<ApplicationId, DhcpConfig>(APP_SUBJECT_FACTORY,
117 DhcpConfig.class,
samanwita palf28207b2015-09-04 10:41:56 -0700118 "dhcp") {
119 @Override
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700120 public DhcpConfig createConfig() {
121 return new DhcpConfig();
samanwita palf28207b2015-09-04 10:41:56 -0700122 }
samanwita palf28207b2015-09-04 10:41:56 -0700123 }
124 );
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected NetworkConfigRegistry cfgService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected PacketService packetService;
130
Jonathan Hartb35540a2015-11-17 09:30:56 -0800131 private DhcpPacketProcessor processor = new DhcpPacketProcessor();
samanwita palf28207b2015-09-04 10:41:56 -0700132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected CoreService coreService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700137 protected DhcpStore dhcpStore;
samanwita palf28207b2015-09-04 10:41:56 -0700138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected HostProviderRegistry hostProviderRegistry;
141
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected ComponentConfigService componentConfigService;
144
145 @Property(name = ALLOW_HOST_DISCOVERY, boolValue = DEFAULT_ALLOW_HOST_DISCOVERY,
146 label = "Allow host discovery from DHCP request")
147 private boolean allowHostDiscovery = DEFAULT_ALLOW_HOST_DISCOVERY;
148
samanwita palf28207b2015-09-04 10:41:56 -0700149 protected HostProviderService hostProviderService;
samanwita pal2a313402015-09-14 16:03:22 -0700150 private final HostProvider hostProvider = new InternalHostProvider();
samanwita palf28207b2015-09-04 10:41:56 -0700151 private ApplicationId appId;
152
153 // Hardcoded values are default values.
samanwita palf28207b2015-09-04 10:41:56 -0700154 /**
155 * leaseTime - 10 mins or 600s.
156 * renewalTime - 5 mins or 300s.
157 * rebindingTime - 6 mins or 360s.
158 */
samanwita palf28207b2015-09-04 10:41:56 -0700159 private static int leaseTime = 600;
samanwita palf28207b2015-09-04 10:41:56 -0700160 private static int renewalTime = 300;
samanwita palf28207b2015-09-04 10:41:56 -0700161 private static int rebindingTime = 360;
samanwita palf28207b2015-09-04 10:41:56 -0700162 private static byte packetTTL = (byte) 127;
samanwita pal2a313402015-09-14 16:03:22 -0700163 private static Ip4Address subnetMask = Ip4Address.valueOf("255.0.0.0");
samanwita pal2a313402015-09-14 16:03:22 -0700164 private static Ip4Address broadcastAddress = Ip4Address.valueOf("10.255.255.255");
samanwita pal2a313402015-09-14 16:03:22 -0700165 private static Ip4Address routerAddress = Ip4Address.valueOf("10.0.0.2");
samanwita pal2a313402015-09-14 16:03:22 -0700166 private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700167 private static Ip4Address myIP = Ip4Address.valueOf("10.0.0.2");
Rafal Szalecki9638ef62017-02-14 15:05:51 +0100168 private static MacAddress myMAC = valueOf("4e:4f:4f:4f:4f:4f");
samanwita pala3d1d1c2015-09-25 11:50:15 -0700169 private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
170
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700171 protected ScheduledFuture<?> timeout;
samanwita pal2a313402015-09-14 16:03:22 -0700172 protected static int timerDelay = 2;
samanwita palf28207b2015-09-04 10:41:56 -0700173
174 @Activate
175 protected void activate() {
176 // start the dhcp server
177 appId = coreService.registerApplication("org.onosproject.dhcp");
178
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700179 componentConfigService.registerProperties(getClass());
samanwita palf28207b2015-09-04 10:41:56 -0700180 cfgService.addListener(cfgListener);
181 factories.forEach(cfgService::registerConfigFactory);
Thomas Vachuska00090442015-09-11 18:08:04 -0700182 cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class));
samanwita palf28207b2015-09-04 10:41:56 -0700183 hostProviderService = hostProviderRegistry.register(hostProvider);
Hyunsun Moon4f47a862016-08-18 11:30:59 -0700184 packetService.addProcessor(processor, PacketProcessor.director(1));
samanwita palf28207b2015-09-04 10:41:56 -0700185 requestPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700186 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita palf28207b2015-09-04 10:41:56 -0700187 log.info("Started");
188 }
189
190 @Deactivate
191 protected void deactivate() {
192 cfgService.removeListener(cfgListener);
193 factories.forEach(cfgService::unregisterConfigFactory);
194 packetService.removeProcessor(processor);
195 hostProviderRegistry.unregister(hostProvider);
196 hostProviderService = null;
197 cancelPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700198 timeout.cancel(true);
samanwita palf28207b2015-09-04 10:41:56 -0700199 log.info("Stopped");
200 }
201
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700202 @Modified
203 protected void modified(ComponentContext context) {
204 Dictionary<?, ?> properties = context.getProperties();
205
206 String updatedConfig = Tools.get(properties, ALLOW_HOST_DISCOVERY);
207 if (!Strings.isNullOrEmpty(updatedConfig)) {
208 allowHostDiscovery = Boolean.valueOf(updatedConfig);
209 log.info("Host discovery is set to {}", updatedConfig);
210 }
211
212 log.info("Modified");
213 }
214
samanwita palf28207b2015-09-04 10:41:56 -0700215 /**
216 * Request packet in via PacketService.
217 */
218 private void requestPackets() {
219
220 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
221 .matchEthType(Ethernet.TYPE_IPV4)
222 .matchIPProtocol(IPv4.PROTOCOL_UDP)
223 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
224 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
225 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
226
227 selectorServer = DefaultTrafficSelector.builder()
228 .matchEthType(Ethernet.TYPE_ARP);
229 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
230 }
231
232 /**
233 * Cancel requested packets in via packet service.
234 */
235 private void cancelPackets() {
236 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
237 .matchEthType(Ethernet.TYPE_IPV4)
238 .matchIPProtocol(IPv4.PROTOCOL_UDP)
239 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
240 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
241 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
242
243 selectorServer = DefaultTrafficSelector.builder()
244 .matchEthType(Ethernet.TYPE_ARP);
245 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
246 }
247
248 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700249 public Map<HostId, IpAssignment> listMapping() {
samanwita pal0bff4302015-09-15 13:37:00 -0700250 return dhcpStore.listAssignedMapping();
samanwita palf28207b2015-09-04 10:41:56 -0700251 }
252
253 @Override
254 public int getLeaseTime() {
255 return leaseTime;
256 }
257
258 @Override
259 public int getRenewalTime() {
260 return renewalTime;
261 }
262
263 @Override
264 public int getRebindingTime() {
265 return rebindingTime;
266 }
267
268 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700269 public boolean setStaticMapping(MacAddress macAddress, IpAssignment ipAssignment) {
270 log.debug("setStaticMapping is called with Mac: {} IpAssignment: {}",
271 macAddress, ipAssignment);
272 return dhcpStore.assignStaticIP(macAddress, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700273 }
274
275 @Override
276 public boolean removeStaticMapping(MacAddress macID) {
277 return dhcpStore.removeStaticIP(macID);
278 }
279
280 @Override
281 public Iterable<Ip4Address> getAvailableIPs() {
282 return dhcpStore.getAvailableIPs();
283 }
284
Jonathan Hartb35540a2015-11-17 09:30:56 -0800285 private class DhcpPacketProcessor implements PacketProcessor {
samanwita palf28207b2015-09-04 10:41:56 -0700286
287 /**
288 * Builds the DHCP Reply packet.
289 *
290 * @param packet the incoming Ethernet frame
291 * @param ipOffered the IP offered by the DHCP Server
292 * @param outgoingMessageType the message type of the outgoing packet
293 * @return the Ethernet reply frame
294 */
samanwita pal2a313402015-09-14 16:03:22 -0700295 private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
samanwita palf28207b2015-09-04 10:41:56 -0700296
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700297 // mandatory options
298 // TODO save and get the information below to/from IP assignment
299 Ip4Address dhcpServerReply = myIP;
300 Ip4Address subnetMaskReply = subnetMask;
301 Ip4Address broadcastReply = broadcastAddress;
danielcd9deed2015-10-30 17:16:16 +0900302
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700303 // optional options
304 Optional<Ip4Address> routerAddressReply = Optional.of(routerAddress);
305 Optional<Ip4Address> domainServerReply = Optional.of(domainServer);
danielcd9deed2015-10-30 17:16:16 +0900306
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700307 IpAssignment ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(
308 HostId.hostId(packet.getSourceMAC()));
309
310 if (ipAssignment != null &&
311 ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900312 subnetMaskReply = ipAssignment.subnetMask();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700313 broadcastReply = ipAssignment.broadcast();
314 routerAddressReply = Optional.ofNullable(ipAssignment.routerAddress());
315 domainServerReply = Optional.ofNullable(ipAssignment.domainServer());
danielcd9deed2015-10-30 17:16:16 +0900316 }
317
samanwita palf28207b2015-09-04 10:41:56 -0700318 // Ethernet Frame.
319 Ethernet ethReply = new Ethernet();
320 ethReply.setSourceMACAddress(myMAC);
321 ethReply.setDestinationMACAddress(packet.getSourceMAC());
322 ethReply.setEtherType(Ethernet.TYPE_IPV4);
323 ethReply.setVlanID(packet.getVlanID());
324
325 // IP Packet
326 IPv4 ipv4Packet = (IPv4) packet.getPayload();
327 IPv4 ipv4Reply = new IPv4();
danielcd9deed2015-10-30 17:16:16 +0900328 ipv4Reply.setSourceAddress(dhcpServerReply.toInt());
samanwita pal2a313402015-09-14 16:03:22 -0700329 ipv4Reply.setDestinationAddress(ipOffered.toInt());
samanwita palf28207b2015-09-04 10:41:56 -0700330 ipv4Reply.setTtl(packetTTL);
331
332 // UDP Datagram.
333 UDP udpPacket = (UDP) ipv4Packet.getPayload();
334 UDP udpReply = new UDP();
335 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
336 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
337
338 // DHCP Payload.
339 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
340 DHCP dhcpReply = new DHCP();
341 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700342 dhcpReply.setFlags(dhcpPacket.getFlags());
343 dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
samanwita palf28207b2015-09-04 10:41:56 -0700344 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700345 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
346
347 if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
348 dhcpReply.setYourIPAddress(ipOffered.toInt());
danielcd9deed2015-10-30 17:16:16 +0900349 dhcpReply.setServerIPAddress(dhcpServerReply.toInt());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700350 if (dhcpPacket.getGatewayIPAddress() == 0) {
351 ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
352 }
353 }
samanwita palf28207b2015-09-04 10:41:56 -0700354 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
355 dhcpReply.setHardwareAddressLength((byte) 6);
356
357 // DHCP Options.
358 DHCPOption option = new DHCPOption();
359 List<DHCPOption> optionList = new ArrayList<>();
360
361 // DHCP Message Type.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700362 option.setCode(OptionCode_MessageType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700363 option.setLength((byte) 1);
364 byte[] optionData = {outgoingMessageType};
365 option.setData(optionData);
366 optionList.add(option);
367
368 // DHCP Server Identifier.
369 option = new DHCPOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700370 option.setCode(OptionCode_DHCPServerIp.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700371 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900372 option.setData(dhcpServerReply.toOctets());
samanwita palf28207b2015-09-04 10:41:56 -0700373 optionList.add(option);
374
samanwita pala3d1d1c2015-09-25 11:50:15 -0700375 if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700376 // IP Address Lease Time.
377 option = new DHCPOption();
378 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
379 option.setLength((byte) 4);
daniel02f3af02015-12-17 18:44:52 +0900380 option.setData(ByteBuffer.allocate(4)
381 .putInt(ipAssignment == null ? leaseTime : ipAssignment.leasePeriod()).array());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700382 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700383
samanwita pala3d1d1c2015-09-25 11:50:15 -0700384 // IP Address Renewal Time.
385 option = new DHCPOption();
386 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
387 option.setLength((byte) 4);
388 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
389 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700390
samanwita pala3d1d1c2015-09-25 11:50:15 -0700391 // IP Address Rebinding Time.
392 option = new DHCPOption();
393 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
394 option.setLength((byte) 4);
395 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
396 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700397
samanwita pala3d1d1c2015-09-25 11:50:15 -0700398 // Subnet Mask.
399 option = new DHCPOption();
400 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
401 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900402 option.setData(subnetMaskReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700403 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700404
samanwita pala3d1d1c2015-09-25 11:50:15 -0700405 // Broadcast Address.
406 option = new DHCPOption();
407 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
408 option.setLength((byte) 4);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700409 option.setData(broadcastReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700410 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700411
samanwita pala3d1d1c2015-09-25 11:50:15 -0700412 // Router Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700413 if (routerAddressReply.isPresent()) {
414 option = new DHCPOption();
415 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
416 option.setLength((byte) 4);
417 option.setData(routerAddressReply.get().toOctets());
418 optionList.add(option);
419 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700420
421 // DNS Server Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700422 if (domainServerReply.isPresent()) {
423 option = new DHCPOption();
424 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
425 option.setLength((byte) 4);
426 option.setData(domainServerReply.get().toOctets());
427 optionList.add(option);
428 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700429 }
samanwita palf28207b2015-09-04 10:41:56 -0700430
431 // End Option.
432 option = new DHCPOption();
433 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
434 option.setLength((byte) 1);
435 optionList.add(option);
436
437 dhcpReply.setOptions(optionList);
samanwita palf28207b2015-09-04 10:41:56 -0700438 udpReply.setPayload(dhcpReply);
439 ipv4Reply.setPayload(udpReply);
440 ethReply.setPayload(ipv4Reply);
441
442 return ethReply;
443 }
444
445 /**
446 * Sends the Ethernet reply frame via the Packet Service.
447 *
448 * @param context the context of the incoming frame
449 * @param reply the Ethernet reply frame
450 */
451 private void sendReply(PacketContext context, Ethernet reply) {
452 if (reply != null) {
453 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
454 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
455 builder.setOutput(sourcePoint.port());
Thomas Vachuska00090442015-09-11 18:08:04 -0700456 context.block();
samanwita palf28207b2015-09-04 10:41:56 -0700457 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
samanwita pal8969cbe2015-09-04 13:31:30 -0700458 builder.build(), ByteBuffer.wrap(reply.serialize())));
samanwita palf28207b2015-09-04 10:41:56 -0700459 }
460 }
461
462 /**
463 * Processes the DHCP Payload and initiates a reply to the client.
464 *
465 * @param context context of the incoming message
466 * @param dhcpPayload the extracted DHCP payload
467 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800468 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700469 if (dhcpPayload == null) {
470 log.debug("DHCP packet without payload, do nothing");
471 return;
472 }
473
samanwita palf28207b2015-09-04 10:41:56 -0700474 Ethernet packet = context.inPacket().parsed();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700475 DHCPPacketType incomingPacketType = null;
samanwita palf28207b2015-09-04 10:41:56 -0700476 boolean flagIfRequestedIP = false;
477 boolean flagIfServerIP = false;
478 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
479 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
480
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700481 for (DHCPOption option : dhcpPayload.getOptions()) {
482 if (option.getCode() == OptionCode_MessageType.getValue()) {
483 byte[] data = option.getData();
484 incomingPacketType = DHCPPacketType.getType(data[0]);
samanwita palf28207b2015-09-04 10:41:56 -0700485 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700486 if (option.getCode() == OptionCode_RequestedIP.getValue()) {
487 byte[] data = option.getData();
488 requestedIP = Ip4Address.valueOf(data);
489 flagIfRequestedIP = true;
490 }
491 if (option.getCode() == OptionCode_DHCPServerIp.getValue()) {
492 byte[] data = option.getData();
493 serverIP = Ip4Address.valueOf(data);
494 flagIfServerIP = true;
495 }
496 }
samanwita palf28207b2015-09-04 10:41:56 -0700497
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700498 if (incomingPacketType == null) {
499 log.debug("No incoming packet type specified, ignore it");
500 return;
501 }
samanwita palf28207b2015-09-04 10:41:56 -0700502
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700503 DHCPPacketType outgoingPacketType;
504 MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
505 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
506 HostId hostId = HostId.hostId(clientMac, vlanId);
danielcd9deed2015-10-30 17:16:16 +0900507
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700508 switch (incomingPacketType) {
509 case DHCPDISCOVER:
510 log.trace("DHCP DISCOVER received from {}", hostId);
511 Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
samanwita pal2a313402015-09-14 16:03:22 -0700512 if (ipOffered != null) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700513 Ethernet ethReply = buildReply(
514 packet,
515 ipOffered,
516 (byte) DHCPOFFER.getValue());
samanwita pal2a313402015-09-14 16:03:22 -0700517 sendReply(context, ethReply);
518 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700519 break;
520 case DHCPREQUEST:
521 log.trace("DHCP REQUEST received from {}", hostId);
522 if (flagIfServerIP && !myIP.equals(serverIP)) {
523 return;
524 }
samanwita palf28207b2015-09-04 10:41:56 -0700525
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700526 if (!flagIfRequestedIP) {
527 // this is renew or rebinding request
528 int clientIp = dhcpPayload.getClientIPAddress();
529 requestedIP = Ip4Address.valueOf(clientIp);
530 }
samanwita palf28207b2015-09-04 10:41:56 -0700531
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700532 IpAssignment ipAssignment = IpAssignment.builder()
533 .ipAddress(requestedIP)
534 .leasePeriod(leaseTime)
535 .timestamp(new Date())
536 .assignmentStatus(Option_Requested).build();
daniel877bb2f2015-11-12 21:33:05 +0900537
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700538 if (dhcpStore.assignIP(hostId, ipAssignment)) {
539 outgoingPacketType = DHCPACK;
540 discoverHost(context, requestedIP);
samanwita palf28207b2015-09-04 10:41:56 -0700541 } else {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700542 outgoingPacketType = DHCPNAK;
samanwita palf28207b2015-09-04 10:41:56 -0700543 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700544
545 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
546 sendReply(context, ethReply);
547 break;
548 case DHCPRELEASE:
549 log.trace("DHCP RELEASE received from {}", hostId);
550 Ip4Address releaseIp = dhcpStore.releaseIP(hostId);
551 if (releaseIp != null) {
552 hostProviderService.removeIpFromHost(hostId, releaseIp);
samanwita palc40e5ed2015-09-24 11:01:51 -0700553 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700554 break;
555 default:
556 break;
samanwita palf28207b2015-09-04 10:41:56 -0700557 }
558 }
559
560 /**
561 * Processes the ARP Payload and initiates a reply to the client.
562 *
563 * @param context context of the incoming message
564 * @param packet the ethernet payload
565 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800566 private void processArpPacket(PacketContext context, Ethernet packet) {
samanwita palf28207b2015-09-04 10:41:56 -0700567
568 ARP arpPacket = (ARP) packet.getPayload();
569
570 ARP arpReply = (ARP) arpPacket.clone();
571 arpReply.setOpCode(ARP.OP_REPLY);
572
573 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
574 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
575 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
576 arpReply.setSenderHardwareAddress(myMAC.toBytes());
577
578 // Ethernet Frame.
579 Ethernet ethReply = new Ethernet();
580 ethReply.setSourceMACAddress(myMAC);
581 ethReply.setDestinationMACAddress(packet.getSourceMAC());
582 ethReply.setEtherType(Ethernet.TYPE_ARP);
583 ethReply.setVlanID(packet.getVlanID());
584
585 ethReply.setPayload(arpReply);
586 sendReply(context, ethReply);
587 }
588
589 /**
590 * Integrates hosts learned through DHCP into topology.
591 * @param context context of the incoming message
592 * @param ipAssigned IP Address assigned to the host by DHCP Manager
593 */
594 private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700595 if (!allowHostDiscovery) {
596 // host discovery is not allowed, do nothing
597 return;
598 }
599
samanwita palf28207b2015-09-04 10:41:56 -0700600 Ethernet packet = context.inPacket().parsed();
601 MacAddress mac = packet.getSourceMAC();
602 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
603 HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0);
604
605 Set<IpAddress> ips = new HashSet<>();
606 ips.add(ipAssigned);
607
608 HostId hostId = HostId.hostId(mac, vlanId);
609 DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700610
611 log.info("Discovered host {}", desc);
Ray Milkeydc083442016-02-22 11:27:57 -0800612 hostProviderService.hostDetected(hostId, desc, false);
samanwita palf28207b2015-09-04 10:41:56 -0700613 }
614
615
616 @Override
617 public void process(PacketContext context) {
samanwita palf28207b2015-09-04 10:41:56 -0700618 Ethernet packet = context.inPacket().parsed();
619 if (packet == null) {
620 return;
621 }
622
623 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
624 IPv4 ipv4Packet = (IPv4) packet.getPayload();
625
626 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
627 UDP udpPacket = (UDP) ipv4Packet.getPayload();
628
629 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
630 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
631 // This is meant for the dhcp server so process the packet here.
632
633 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Jonathan Hartb35540a2015-11-17 09:30:56 -0800634 processDhcpPacket(context, dhcpPayload);
samanwita palf28207b2015-09-04 10:41:56 -0700635 }
636 }
637 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
638 ARP arpPacket = (ARP) packet.getPayload();
639
640 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700641 Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
samanwita palf28207b2015-09-04 10:41:56 -0700642
Jonathan Hartb35540a2015-11-17 09:30:56 -0800643 processArpPacket(context, packet);
samanwita palf28207b2015-09-04 10:41:56 -0700644
645 }
646 }
647 }
648 }
649
650 private class InternalConfigListener implements NetworkConfigListener {
651
652 /**
653 * Reconfigures the DHCP Server according to the configuration parameters passed.
654 *
655 * @param cfg configuration object
656 */
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700657 private void reconfigureNetwork(DhcpConfig cfg) {
Thomas Vachuska00090442015-09-11 18:08:04 -0700658 if (cfg == null) {
659 return;
660 }
samanwita palf28207b2015-09-04 10:41:56 -0700661 if (cfg.ip() != null) {
662 myIP = cfg.ip();
663 }
664 if (cfg.mac() != null) {
samanwita pal2a313402015-09-14 16:03:22 -0700665 myMAC = cfg.mac();
samanwita palf28207b2015-09-04 10:41:56 -0700666 }
667 if (cfg.subnetMask() != null) {
668 subnetMask = cfg.subnetMask();
669 }
670 if (cfg.broadcastAddress() != null) {
671 broadcastAddress = cfg.broadcastAddress();
672 }
673 if (cfg.routerAddress() != null) {
674 routerAddress = cfg.routerAddress();
675 }
676 if (cfg.domainServer() != null) {
677 domainServer = cfg.domainServer();
678 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700679 if (cfg.ttl() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700680 packetTTL = (byte) cfg.ttl();
samanwita palf28207b2015-09-04 10:41:56 -0700681 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700682 if (cfg.leaseTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700683 leaseTime = cfg.leaseTime();
samanwita palf28207b2015-09-04 10:41:56 -0700684 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700685 if (cfg.renewTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700686 renewalTime = cfg.renewTime();
samanwita palf28207b2015-09-04 10:41:56 -0700687 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700688 if (cfg.rebindTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700689 rebindingTime = cfg.rebindTime();
690 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700691 if (cfg.defaultTimeout() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700692 dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout());
693 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700694 if (cfg.timerDelay() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700695 timerDelay = cfg.timerDelay();
696 }
697 if ((cfg.startIp() != null) && (cfg.endIp() != null)) {
698 dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp());
samanwita palf28207b2015-09-04 10:41:56 -0700699 }
700 }
701
samanwita palf28207b2015-09-04 10:41:56 -0700702
703 @Override
704 public void event(NetworkConfigEvent event) {
705
706 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
samanwita pal2a313402015-09-14 16:03:22 -0700707 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
708 event.configClass().equals(DhcpConfig.class)) {
709
710 DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class);
711 reconfigureNetwork(cfg);
712 log.info("Reconfigured");
samanwita palf28207b2015-09-04 10:41:56 -0700713 }
714 }
715 }
716
717 private class InternalHostProvider extends AbstractProvider implements HostProvider {
718
719 /**
720 * Creates a provider with the supplier identifier.
721 */
722 protected InternalHostProvider() {
723 super(PID);
724 }
725
726 @Override
727 public void triggerProbe(Host host) {
728 // nothing to do
729 }
730 }
samanwita pal2a313402015-09-14 16:03:22 -0700731
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700732 private class PurgeListTask implements Runnable {
samanwita pal2a313402015-09-14 16:03:22 -0700733
734 @Override
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700735 public void run() {
samanwita pal2a313402015-09-14 16:03:22 -0700736 IpAssignment ipAssignment;
737 Date dateNow = new Date();
738
samanwita pal0bff4302015-09-15 13:37:00 -0700739 Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping();
samanwita pal2a313402015-09-14 16:03:22 -0700740 for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) {
741 ipAssignment = entry.getValue();
742
743 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
744 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
745 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
746
samanwita palc40e5ed2015-09-24 11:01:51 -0700747 Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
748 if (ip4Address != null) {
749 hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
750 }
samanwita pal2a313402015-09-14 16:03:22 -0700751 }
752 }
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700753 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita pal2a313402015-09-14 16:03:22 -0700754 }
755 }
Jonathan Hartb35540a2015-11-17 09:30:56 -0800756}