blob: f18b861c824b91ce55ebda2cad720de01e070ba5 [file] [log] [blame]
samanwita palf28207b2015-09-04 10:41:56 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
samanwita palf28207b2015-09-04 10:41:56 -070031import org.onlab.packet.Ethernet;
32import org.onlab.packet.IPv4;
33import org.onlab.packet.Ip4Address;
34import org.onlab.packet.IpAddress;
35import org.onlab.packet.MacAddress;
36import org.onlab.packet.TpPort;
37import org.onlab.packet.UDP;
38import org.onlab.packet.VlanId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070039import org.onlab.packet.dhcp.DhcpOption;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070040import org.onlab.util.SharedScheduledExecutors;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070041import org.onlab.util.Tools;
42import org.onosproject.cfg.ComponentConfigService;
samanwita palf28207b2015-09-04 10:41:56 -070043import org.onosproject.core.ApplicationId;
44import org.onosproject.core.CoreService;
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070045import org.onosproject.dhcp.DhcpService;
46import org.onosproject.dhcp.DhcpStore;
47import org.onosproject.dhcp.IpAssignment;
samanwita palf28207b2015-09-04 10:41:56 -070048import org.onosproject.net.ConnectPoint;
49import org.onosproject.net.Host;
50import org.onosproject.net.HostId;
51import org.onosproject.net.HostLocation;
samanwita pal2a313402015-09-14 16:03:22 -070052import org.onosproject.net.config.ConfigFactory;
53import org.onosproject.net.config.NetworkConfigEvent;
54import org.onosproject.net.config.NetworkConfigListener;
55import org.onosproject.net.config.NetworkConfigRegistry;
samanwita palf28207b2015-09-04 10:41:56 -070056import org.onosproject.net.flow.DefaultTrafficSelector;
57import org.onosproject.net.flow.DefaultTrafficTreatment;
58import org.onosproject.net.flow.TrafficSelector;
59import org.onosproject.net.flow.TrafficTreatment;
60import org.onosproject.net.host.DefaultHostDescription;
61import org.onosproject.net.host.HostProvider;
62import org.onosproject.net.host.HostProviderRegistry;
63import org.onosproject.net.host.HostProviderService;
64import org.onosproject.net.packet.DefaultOutboundPacket;
65import org.onosproject.net.packet.PacketContext;
66import org.onosproject.net.packet.PacketPriority;
67import org.onosproject.net.packet.PacketProcessor;
68import org.onosproject.net.packet.PacketService;
69import org.onosproject.net.provider.AbstractProvider;
70import org.onosproject.net.provider.ProviderId;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070071import org.osgi.service.component.ComponentContext;
samanwita palf28207b2015-09-04 10:41:56 -070072import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
75import java.nio.ByteBuffer;
76import java.util.ArrayList;
samanwita pal2a313402015-09-14 16:03:22 -070077import java.util.Date;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070078import java.util.Dictionary;
samanwita palf28207b2015-09-04 10:41:56 -070079import java.util.HashSet;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070080
samanwita palf28207b2015-09-04 10:41:56 -070081import 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;
samanwita palf28207b2015-09-04 10:41:56 -070092import static org.onlab.packet.MacAddress.valueOf;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070093import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
94import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Requested;
samanwita palf28207b2015-09-04 10:41:56 -070095import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
96
97/**
98 * Skeletal ONOS DHCP Server application.
99 */
100@Component(immediate = true)
101@Service
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700102public class DhcpManager implements DhcpService {
samanwita palf28207b2015-09-04 10:41:56 -0700103
104 private static final ProviderId PID = new ProviderId("of", "org.onosproject.dhcp", true);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700105 private static final String ALLOW_HOST_DISCOVERY = "allowHostDiscovery";
106 private static final boolean DEFAULT_ALLOW_HOST_DISCOVERY = false;
107
samanwita palf28207b2015-09-04 10:41:56 -0700108 private final Logger log = LoggerFactory.getLogger(getClass());
109
Thomas Vachuska00090442015-09-11 18:08:04 -0700110 private final InternalConfigListener cfgListener = new InternalConfigListener();
samanwita palf28207b2015-09-04 10:41:56 -0700111
112 private final Set<ConfigFactory> factories = ImmutableSet.of(
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700113 new ConfigFactory<ApplicationId, DhcpConfig>(APP_SUBJECT_FACTORY,
114 DhcpConfig.class,
samanwita palf28207b2015-09-04 10:41:56 -0700115 "dhcp") {
116 @Override
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700117 public DhcpConfig createConfig() {
118 return new DhcpConfig();
samanwita palf28207b2015-09-04 10:41:56 -0700119 }
samanwita palf28207b2015-09-04 10:41:56 -0700120 }
121 );
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected NetworkConfigRegistry cfgService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected PacketService packetService;
127
Jonathan Hartb35540a2015-11-17 09:30:56 -0800128 private DhcpPacketProcessor processor = new DhcpPacketProcessor();
samanwita palf28207b2015-09-04 10:41:56 -0700129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected CoreService coreService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700134 protected DhcpStore dhcpStore;
samanwita palf28207b2015-09-04 10:41:56 -0700135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected HostProviderRegistry hostProviderRegistry;
138
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected ComponentConfigService componentConfigService;
141
142 @Property(name = ALLOW_HOST_DISCOVERY, boolValue = DEFAULT_ALLOW_HOST_DISCOVERY,
143 label = "Allow host discovery from DHCP request")
144 private boolean allowHostDiscovery = DEFAULT_ALLOW_HOST_DISCOVERY;
145
samanwita palf28207b2015-09-04 10:41:56 -0700146 protected HostProviderService hostProviderService;
samanwita pal2a313402015-09-14 16:03:22 -0700147 private final HostProvider hostProvider = new InternalHostProvider();
samanwita palf28207b2015-09-04 10:41:56 -0700148 private ApplicationId appId;
149
150 // Hardcoded values are default values.
samanwita palf28207b2015-09-04 10:41:56 -0700151 /**
152 * leaseTime - 10 mins or 600s.
153 * renewalTime - 5 mins or 300s.
154 * rebindingTime - 6 mins or 360s.
155 */
samanwita palf28207b2015-09-04 10:41:56 -0700156 private static int leaseTime = 600;
samanwita palf28207b2015-09-04 10:41:56 -0700157 private static int renewalTime = 300;
samanwita palf28207b2015-09-04 10:41:56 -0700158 private static int rebindingTime = 360;
samanwita palf28207b2015-09-04 10:41:56 -0700159 private static byte packetTTL = (byte) 127;
samanwita pal2a313402015-09-14 16:03:22 -0700160 private static Ip4Address subnetMask = Ip4Address.valueOf("255.0.0.0");
samanwita pal2a313402015-09-14 16:03:22 -0700161 private static Ip4Address broadcastAddress = Ip4Address.valueOf("10.255.255.255");
samanwita pal2a313402015-09-14 16:03:22 -0700162 private static Ip4Address routerAddress = Ip4Address.valueOf("10.0.0.2");
samanwita pal2a313402015-09-14 16:03:22 -0700163 private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700164 private static Ip4Address myIP = Ip4Address.valueOf("10.0.0.2");
Rafal Szalecki9638ef62017-02-14 15:05:51 +0100165 private static MacAddress myMAC = valueOf("4e:4f:4f:4f:4f:4f");
samanwita pala3d1d1c2015-09-25 11:50:15 -0700166 private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
167
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700168 protected ScheduledFuture<?> timeout;
samanwita pal2a313402015-09-14 16:03:22 -0700169 protected static int timerDelay = 2;
samanwita palf28207b2015-09-04 10:41:56 -0700170
171 @Activate
172 protected void activate() {
173 // start the dhcp server
174 appId = coreService.registerApplication("org.onosproject.dhcp");
175
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700176 componentConfigService.registerProperties(getClass());
samanwita palf28207b2015-09-04 10:41:56 -0700177 cfgService.addListener(cfgListener);
178 factories.forEach(cfgService::registerConfigFactory);
Thomas Vachuska00090442015-09-11 18:08:04 -0700179 cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class));
samanwita palf28207b2015-09-04 10:41:56 -0700180 hostProviderService = hostProviderRegistry.register(hostProvider);
Hyunsun Moon4f47a862016-08-18 11:30:59 -0700181 packetService.addProcessor(processor, PacketProcessor.director(1));
samanwita palf28207b2015-09-04 10:41:56 -0700182 requestPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700183 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita palf28207b2015-09-04 10:41:56 -0700184 log.info("Started");
185 }
186
187 @Deactivate
188 protected void deactivate() {
Thomas Vachuskafa615492018-03-19 09:11:51 -0700189 componentConfigService.unregisterProperties(getClass(), false);
samanwita palf28207b2015-09-04 10:41:56 -0700190 cfgService.removeListener(cfgListener);
191 factories.forEach(cfgService::unregisterConfigFactory);
192 packetService.removeProcessor(processor);
193 hostProviderRegistry.unregister(hostProvider);
194 hostProviderService = null;
195 cancelPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700196 timeout.cancel(true);
samanwita palf28207b2015-09-04 10:41:56 -0700197 log.info("Stopped");
198 }
199
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700200 @Modified
201 protected void modified(ComponentContext context) {
202 Dictionary<?, ?> properties = context.getProperties();
203
204 String updatedConfig = Tools.get(properties, ALLOW_HOST_DISCOVERY);
205 if (!Strings.isNullOrEmpty(updatedConfig)) {
206 allowHostDiscovery = Boolean.valueOf(updatedConfig);
207 log.info("Host discovery is set to {}", updatedConfig);
208 }
209
210 log.info("Modified");
211 }
212
samanwita palf28207b2015-09-04 10:41:56 -0700213 /**
214 * Request packet in via PacketService.
215 */
216 private void requestPackets() {
217
218 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
219 .matchEthType(Ethernet.TYPE_IPV4)
220 .matchIPProtocol(IPv4.PROTOCOL_UDP)
221 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
222 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
223 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
224
225 selectorServer = DefaultTrafficSelector.builder()
226 .matchEthType(Ethernet.TYPE_ARP);
227 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
228 }
229
230 /**
231 * Cancel requested packets in via packet service.
232 */
233 private void cancelPackets() {
234 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
235 .matchEthType(Ethernet.TYPE_IPV4)
236 .matchIPProtocol(IPv4.PROTOCOL_UDP)
237 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
238 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
239 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
240
241 selectorServer = DefaultTrafficSelector.builder()
242 .matchEthType(Ethernet.TYPE_ARP);
243 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
244 }
245
246 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700247 public Map<HostId, IpAssignment> listMapping() {
samanwita pal0bff4302015-09-15 13:37:00 -0700248 return dhcpStore.listAssignedMapping();
samanwita palf28207b2015-09-04 10:41:56 -0700249 }
250
251 @Override
252 public int getLeaseTime() {
253 return leaseTime;
254 }
255
256 @Override
257 public int getRenewalTime() {
258 return renewalTime;
259 }
260
261 @Override
262 public int getRebindingTime() {
263 return rebindingTime;
264 }
265
266 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700267 public boolean setStaticMapping(MacAddress macAddress, IpAssignment ipAssignment) {
268 log.debug("setStaticMapping is called with Mac: {} IpAssignment: {}",
269 macAddress, ipAssignment);
270 return dhcpStore.assignStaticIP(macAddress, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700271 }
272
273 @Override
274 public boolean removeStaticMapping(MacAddress macID) {
275 return dhcpStore.removeStaticIP(macID);
276 }
277
278 @Override
279 public Iterable<Ip4Address> getAvailableIPs() {
280 return dhcpStore.getAvailableIPs();
281 }
282
Jonathan Hartb35540a2015-11-17 09:30:56 -0800283 private class DhcpPacketProcessor implements PacketProcessor {
samanwita palf28207b2015-09-04 10:41:56 -0700284
285 /**
286 * Builds the DHCP Reply packet.
287 *
288 * @param packet the incoming Ethernet frame
289 * @param ipOffered the IP offered by the DHCP Server
290 * @param outgoingMessageType the message type of the outgoing packet
291 * @return the Ethernet reply frame
292 */
samanwita pal2a313402015-09-14 16:03:22 -0700293 private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
samanwita palf28207b2015-09-04 10:41:56 -0700294
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700295 // mandatory options
296 // TODO save and get the information below to/from IP assignment
297 Ip4Address dhcpServerReply = myIP;
298 Ip4Address subnetMaskReply = subnetMask;
299 Ip4Address broadcastReply = broadcastAddress;
danielcd9deed2015-10-30 17:16:16 +0900300
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700301 // optional options
302 Optional<Ip4Address> routerAddressReply = Optional.of(routerAddress);
303 Optional<Ip4Address> domainServerReply = Optional.of(domainServer);
danielcd9deed2015-10-30 17:16:16 +0900304
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700305 IpAssignment ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(
306 HostId.hostId(packet.getSourceMAC()));
307
308 if (ipAssignment != null &&
309 ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900310 subnetMaskReply = ipAssignment.subnetMask();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700311 broadcastReply = ipAssignment.broadcast();
312 routerAddressReply = Optional.ofNullable(ipAssignment.routerAddress());
313 domainServerReply = Optional.ofNullable(ipAssignment.domainServer());
danielcd9deed2015-10-30 17:16:16 +0900314 }
315
samanwita palf28207b2015-09-04 10:41:56 -0700316 // Ethernet Frame.
317 Ethernet ethReply = new Ethernet();
318 ethReply.setSourceMACAddress(myMAC);
319 ethReply.setDestinationMACAddress(packet.getSourceMAC());
320 ethReply.setEtherType(Ethernet.TYPE_IPV4);
321 ethReply.setVlanID(packet.getVlanID());
322
323 // IP Packet
324 IPv4 ipv4Packet = (IPv4) packet.getPayload();
325 IPv4 ipv4Reply = new IPv4();
danielcd9deed2015-10-30 17:16:16 +0900326 ipv4Reply.setSourceAddress(dhcpServerReply.toInt());
samanwita pal2a313402015-09-14 16:03:22 -0700327 ipv4Reply.setDestinationAddress(ipOffered.toInt());
samanwita palf28207b2015-09-04 10:41:56 -0700328 ipv4Reply.setTtl(packetTTL);
329
330 // UDP Datagram.
331 UDP udpPacket = (UDP) ipv4Packet.getPayload();
332 UDP udpReply = new UDP();
333 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
334 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
335
336 // DHCP Payload.
337 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
338 DHCP dhcpReply = new DHCP();
339 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700340 dhcpReply.setFlags(dhcpPacket.getFlags());
341 dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
samanwita palf28207b2015-09-04 10:41:56 -0700342 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700343 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
344
Yi Tsengc7403c22017-06-19 16:23:22 -0700345 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700346 dhcpReply.setYourIPAddress(ipOffered.toInt());
danielcd9deed2015-10-30 17:16:16 +0900347 dhcpReply.setServerIPAddress(dhcpServerReply.toInt());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700348 if (dhcpPacket.getGatewayIPAddress() == 0) {
349 ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
350 }
351 }
samanwita palf28207b2015-09-04 10:41:56 -0700352 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
353 dhcpReply.setHardwareAddressLength((byte) 6);
354
355 // DHCP Options.
Yi Tsengc7403c22017-06-19 16:23:22 -0700356 DhcpOption option = new DhcpOption();
357 List<DhcpOption> optionList = new ArrayList<>();
samanwita palf28207b2015-09-04 10:41:56 -0700358
359 // DHCP Message Type.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700360 option.setCode(OptionCode_MessageType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700361 option.setLength((byte) 1);
362 byte[] optionData = {outgoingMessageType};
363 option.setData(optionData);
364 optionList.add(option);
365
366 // DHCP Server Identifier.
Yi Tsengc7403c22017-06-19 16:23:22 -0700367 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700368 option.setCode(OptionCode_DHCPServerIp.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700369 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900370 option.setData(dhcpServerReply.toOctets());
samanwita palf28207b2015-09-04 10:41:56 -0700371 optionList.add(option);
372
Yi Tsengc7403c22017-06-19 16:23:22 -0700373 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700374 // IP Address Lease Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700375 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700376 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
377 option.setLength((byte) 4);
daniel02f3af02015-12-17 18:44:52 +0900378 option.setData(ByteBuffer.allocate(4)
379 .putInt(ipAssignment == null ? leaseTime : ipAssignment.leasePeriod()).array());
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 // IP Address Renewal Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700383 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700384 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
385 option.setLength((byte) 4);
386 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
387 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700388
samanwita pala3d1d1c2015-09-25 11:50:15 -0700389 // IP Address Rebinding Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700390 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700391 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
392 option.setLength((byte) 4);
393 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
394 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700395
samanwita pala3d1d1c2015-09-25 11:50:15 -0700396 // Subnet Mask.
Yi Tsengc7403c22017-06-19 16:23:22 -0700397 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700398 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
399 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900400 option.setData(subnetMaskReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700401 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700402
samanwita pala3d1d1c2015-09-25 11:50:15 -0700403 // Broadcast Address.
Yi Tsengc7403c22017-06-19 16:23:22 -0700404 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700405 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
406 option.setLength((byte) 4);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700407 option.setData(broadcastReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700408 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700409
samanwita pala3d1d1c2015-09-25 11:50:15 -0700410 // Router Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700411 if (routerAddressReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700412 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700413 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
414 option.setLength((byte) 4);
415 option.setData(routerAddressReply.get().toOctets());
416 optionList.add(option);
417 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700418
419 // DNS Server Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700420 if (domainServerReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700421 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700422 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
423 option.setLength((byte) 4);
424 option.setData(domainServerReply.get().toOctets());
425 optionList.add(option);
426 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700427 }
samanwita palf28207b2015-09-04 10:41:56 -0700428
429 // End Option.
Yi Tsengc7403c22017-06-19 16:23:22 -0700430 option = new DhcpOption();
samanwita palf28207b2015-09-04 10:41:56 -0700431 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
432 option.setLength((byte) 1);
433 optionList.add(option);
434
435 dhcpReply.setOptions(optionList);
samanwita palf28207b2015-09-04 10:41:56 -0700436 udpReply.setPayload(dhcpReply);
437 ipv4Reply.setPayload(udpReply);
438 ethReply.setPayload(ipv4Reply);
439
440 return ethReply;
441 }
442
443 /**
444 * Sends the Ethernet reply frame via the Packet Service.
445 *
446 * @param context the context of the incoming frame
447 * @param reply the Ethernet reply frame
448 */
449 private void sendReply(PacketContext context, Ethernet reply) {
450 if (reply != null) {
451 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
452 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
453 builder.setOutput(sourcePoint.port());
Thomas Vachuska00090442015-09-11 18:08:04 -0700454 context.block();
samanwita palf28207b2015-09-04 10:41:56 -0700455 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
samanwita pal8969cbe2015-09-04 13:31:30 -0700456 builder.build(), ByteBuffer.wrap(reply.serialize())));
samanwita palf28207b2015-09-04 10:41:56 -0700457 }
458 }
459
460 /**
461 * Processes the DHCP Payload and initiates a reply to the client.
462 *
463 * @param context context of the incoming message
464 * @param dhcpPayload the extracted DHCP payload
465 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800466 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700467 if (dhcpPayload == null) {
468 log.debug("DHCP packet without payload, do nothing");
469 return;
470 }
471
samanwita palf28207b2015-09-04 10:41:56 -0700472 Ethernet packet = context.inPacket().parsed();
Yi Tsengc7403c22017-06-19 16:23:22 -0700473 DHCP.MsgType incomingPacketType = null;
samanwita palf28207b2015-09-04 10:41:56 -0700474 boolean flagIfRequestedIP = false;
475 boolean flagIfServerIP = false;
476 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
477 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
478
Yi Tsengc7403c22017-06-19 16:23:22 -0700479 for (DhcpOption option : dhcpPayload.getOptions()) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700480 if (option.getCode() == OptionCode_MessageType.getValue()) {
481 byte[] data = option.getData();
Yi Tsengc7403c22017-06-19 16:23:22 -0700482 incomingPacketType = DHCP.MsgType.getType(data[0]);
samanwita palf28207b2015-09-04 10:41:56 -0700483 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700484 if (option.getCode() == OptionCode_RequestedIP.getValue()) {
485 byte[] data = option.getData();
486 requestedIP = Ip4Address.valueOf(data);
487 flagIfRequestedIP = true;
488 }
489 if (option.getCode() == OptionCode_DHCPServerIp.getValue()) {
490 byte[] data = option.getData();
491 serverIP = Ip4Address.valueOf(data);
492 flagIfServerIP = true;
493 }
494 }
samanwita palf28207b2015-09-04 10:41:56 -0700495
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700496 if (incomingPacketType == null) {
497 log.debug("No incoming packet type specified, ignore it");
498 return;
499 }
samanwita palf28207b2015-09-04 10:41:56 -0700500
Yi Tsengc7403c22017-06-19 16:23:22 -0700501 DHCP.MsgType outgoingPacketType;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700502 MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
503 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
504 HostId hostId = HostId.hostId(clientMac, vlanId);
danielcd9deed2015-10-30 17:16:16 +0900505
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700506 switch (incomingPacketType) {
507 case DHCPDISCOVER:
508 log.trace("DHCP DISCOVER received from {}", hostId);
509 Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
samanwita pal2a313402015-09-14 16:03:22 -0700510 if (ipOffered != null) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700511 Ethernet ethReply = buildReply(
512 packet,
513 ipOffered,
Yi Tsengc7403c22017-06-19 16:23:22 -0700514 (byte) DHCP.MsgType.DHCPOFFER.getValue());
samanwita pal2a313402015-09-14 16:03:22 -0700515 sendReply(context, ethReply);
516 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700517 break;
518 case DHCPREQUEST:
519 log.trace("DHCP REQUEST received from {}", hostId);
520 if (flagIfServerIP && !myIP.equals(serverIP)) {
521 return;
522 }
samanwita palf28207b2015-09-04 10:41:56 -0700523
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700524 if (!flagIfRequestedIP) {
525 // this is renew or rebinding request
526 int clientIp = dhcpPayload.getClientIPAddress();
527 requestedIP = Ip4Address.valueOf(clientIp);
528 }
samanwita palf28207b2015-09-04 10:41:56 -0700529
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700530 IpAssignment ipAssignment = IpAssignment.builder()
531 .ipAddress(requestedIP)
532 .leasePeriod(leaseTime)
533 .timestamp(new Date())
534 .assignmentStatus(Option_Requested).build();
daniel877bb2f2015-11-12 21:33:05 +0900535
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700536 if (dhcpStore.assignIP(hostId, ipAssignment)) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700537 outgoingPacketType = DHCP.MsgType.DHCPACK;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700538 discoverHost(context, requestedIP);
samanwita palf28207b2015-09-04 10:41:56 -0700539 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700540 outgoingPacketType = DHCP.MsgType.DHCPNAK;
samanwita palf28207b2015-09-04 10:41:56 -0700541 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700542
543 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
544 sendReply(context, ethReply);
545 break;
546 case DHCPRELEASE:
547 log.trace("DHCP RELEASE received from {}", hostId);
548 Ip4Address releaseIp = dhcpStore.releaseIP(hostId);
549 if (releaseIp != null) {
550 hostProviderService.removeIpFromHost(hostId, releaseIp);
samanwita palc40e5ed2015-09-24 11:01:51 -0700551 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700552 break;
553 default:
554 break;
samanwita palf28207b2015-09-04 10:41:56 -0700555 }
556 }
557
558 /**
559 * Processes the ARP Payload and initiates a reply to the client.
560 *
561 * @param context context of the incoming message
562 * @param packet the ethernet payload
563 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800564 private void processArpPacket(PacketContext context, Ethernet packet) {
samanwita palf28207b2015-09-04 10:41:56 -0700565
566 ARP arpPacket = (ARP) packet.getPayload();
567
Ray Milkeyf0c47612017-09-28 11:29:38 -0700568 ARP arpReply = arpPacket.duplicate();
samanwita palf28207b2015-09-04 10:41:56 -0700569 arpReply.setOpCode(ARP.OP_REPLY);
570
571 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
572 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
573 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
574 arpReply.setSenderHardwareAddress(myMAC.toBytes());
575
576 // Ethernet Frame.
577 Ethernet ethReply = new Ethernet();
578 ethReply.setSourceMACAddress(myMAC);
579 ethReply.setDestinationMACAddress(packet.getSourceMAC());
580 ethReply.setEtherType(Ethernet.TYPE_ARP);
581 ethReply.setVlanID(packet.getVlanID());
582
583 ethReply.setPayload(arpReply);
584 sendReply(context, ethReply);
585 }
586
587 /**
588 * Integrates hosts learned through DHCP into topology.
589 * @param context context of the incoming message
590 * @param ipAssigned IP Address assigned to the host by DHCP Manager
591 */
592 private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700593 if (!allowHostDiscovery) {
594 // host discovery is not allowed, do nothing
595 return;
596 }
597
samanwita palf28207b2015-09-04 10:41:56 -0700598 Ethernet packet = context.inPacket().parsed();
599 MacAddress mac = packet.getSourceMAC();
600 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
601 HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0);
602
603 Set<IpAddress> ips = new HashSet<>();
604 ips.add(ipAssigned);
605
606 HostId hostId = HostId.hostId(mac, vlanId);
607 DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700608
609 log.info("Discovered host {}", desc);
Ray Milkeydc083442016-02-22 11:27:57 -0800610 hostProviderService.hostDetected(hostId, desc, false);
samanwita palf28207b2015-09-04 10:41:56 -0700611 }
612
613
614 @Override
615 public void process(PacketContext context) {
samanwita palf28207b2015-09-04 10:41:56 -0700616 Ethernet packet = context.inPacket().parsed();
617 if (packet == null) {
618 return;
619 }
620
621 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
622 IPv4 ipv4Packet = (IPv4) packet.getPayload();
623
624 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
625 UDP udpPacket = (UDP) ipv4Packet.getPayload();
626
627 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
628 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
629 // This is meant for the dhcp server so process the packet here.
630
631 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Jonathan Hartb35540a2015-11-17 09:30:56 -0800632 processDhcpPacket(context, dhcpPayload);
samanwita palf28207b2015-09-04 10:41:56 -0700633 }
634 }
635 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
636 ARP arpPacket = (ARP) packet.getPayload();
637
638 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700639 Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
samanwita palf28207b2015-09-04 10:41:56 -0700640
Jonathan Hartb35540a2015-11-17 09:30:56 -0800641 processArpPacket(context, packet);
samanwita palf28207b2015-09-04 10:41:56 -0700642
643 }
644 }
645 }
646 }
647
648 private class InternalConfigListener implements NetworkConfigListener {
649
650 /**
651 * Reconfigures the DHCP Server according to the configuration parameters passed.
652 *
653 * @param cfg configuration object
654 */
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700655 private void reconfigureNetwork(DhcpConfig cfg) {
Thomas Vachuska00090442015-09-11 18:08:04 -0700656 if (cfg == null) {
657 return;
658 }
samanwita palf28207b2015-09-04 10:41:56 -0700659 if (cfg.ip() != null) {
660 myIP = cfg.ip();
661 }
662 if (cfg.mac() != null) {
samanwita pal2a313402015-09-14 16:03:22 -0700663 myMAC = cfg.mac();
samanwita palf28207b2015-09-04 10:41:56 -0700664 }
665 if (cfg.subnetMask() != null) {
666 subnetMask = cfg.subnetMask();
667 }
668 if (cfg.broadcastAddress() != null) {
669 broadcastAddress = cfg.broadcastAddress();
670 }
671 if (cfg.routerAddress() != null) {
672 routerAddress = cfg.routerAddress();
673 }
674 if (cfg.domainServer() != null) {
675 domainServer = cfg.domainServer();
676 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700677 if (cfg.ttl() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700678 packetTTL = (byte) cfg.ttl();
samanwita palf28207b2015-09-04 10:41:56 -0700679 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700680 if (cfg.leaseTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700681 leaseTime = cfg.leaseTime();
samanwita palf28207b2015-09-04 10:41:56 -0700682 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700683 if (cfg.renewTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700684 renewalTime = cfg.renewTime();
samanwita palf28207b2015-09-04 10:41:56 -0700685 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700686 if (cfg.rebindTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700687 rebindingTime = cfg.rebindTime();
688 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700689 if (cfg.defaultTimeout() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700690 dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout());
691 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700692 if (cfg.timerDelay() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700693 timerDelay = cfg.timerDelay();
694 }
695 if ((cfg.startIp() != null) && (cfg.endIp() != null)) {
696 dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp());
samanwita palf28207b2015-09-04 10:41:56 -0700697 }
698 }
699
samanwita palf28207b2015-09-04 10:41:56 -0700700
701 @Override
702 public void event(NetworkConfigEvent event) {
703
704 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
samanwita pal2a313402015-09-14 16:03:22 -0700705 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
706 event.configClass().equals(DhcpConfig.class)) {
707
708 DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class);
709 reconfigureNetwork(cfg);
710 log.info("Reconfigured");
samanwita palf28207b2015-09-04 10:41:56 -0700711 }
712 }
713 }
714
715 private class InternalHostProvider extends AbstractProvider implements HostProvider {
716
717 /**
718 * Creates a provider with the supplier identifier.
719 */
720 protected InternalHostProvider() {
721 super(PID);
722 }
723
724 @Override
725 public void triggerProbe(Host host) {
726 // nothing to do
727 }
728 }
samanwita pal2a313402015-09-14 16:03:22 -0700729
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700730 private class PurgeListTask implements Runnable {
samanwita pal2a313402015-09-14 16:03:22 -0700731
732 @Override
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700733 public void run() {
samanwita pal2a313402015-09-14 16:03:22 -0700734 IpAssignment ipAssignment;
735 Date dateNow = new Date();
736
samanwita pal0bff4302015-09-15 13:37:00 -0700737 Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping();
samanwita pal2a313402015-09-14 16:03:22 -0700738 for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) {
739 ipAssignment = entry.getValue();
740
741 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
742 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
743 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
744
samanwita palc40e5ed2015-09-24 11:01:51 -0700745 Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
746 if (ip4Address != null) {
747 hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
748 }
samanwita pal2a313402015-09-14 16:03:22 -0700749 }
750 }
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700751 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita pal2a313402015-09-14 16:03:22 -0700752 }
753 }
Jonathan Hartb35540a2015-11-17 09:30:56 -0800754}