blob: 3ec104af8d2fb6058e0dbaeadd020dcb956c3304 [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() {
189 cfgService.removeListener(cfgListener);
190 factories.forEach(cfgService::unregisterConfigFactory);
191 packetService.removeProcessor(processor);
192 hostProviderRegistry.unregister(hostProvider);
193 hostProviderService = null;
194 cancelPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700195 timeout.cancel(true);
samanwita palf28207b2015-09-04 10:41:56 -0700196 log.info("Stopped");
197 }
198
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700199 @Modified
200 protected void modified(ComponentContext context) {
201 Dictionary<?, ?> properties = context.getProperties();
202
203 String updatedConfig = Tools.get(properties, ALLOW_HOST_DISCOVERY);
204 if (!Strings.isNullOrEmpty(updatedConfig)) {
205 allowHostDiscovery = Boolean.valueOf(updatedConfig);
206 log.info("Host discovery is set to {}", updatedConfig);
207 }
208
209 log.info("Modified");
210 }
211
samanwita palf28207b2015-09-04 10:41:56 -0700212 /**
213 * Request packet in via PacketService.
214 */
215 private void requestPackets() {
216
217 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
218 .matchEthType(Ethernet.TYPE_IPV4)
219 .matchIPProtocol(IPv4.PROTOCOL_UDP)
220 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
221 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
222 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
223
224 selectorServer = DefaultTrafficSelector.builder()
225 .matchEthType(Ethernet.TYPE_ARP);
226 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
227 }
228
229 /**
230 * Cancel requested packets in via packet service.
231 */
232 private void cancelPackets() {
233 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
234 .matchEthType(Ethernet.TYPE_IPV4)
235 .matchIPProtocol(IPv4.PROTOCOL_UDP)
236 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
237 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
238 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
239
240 selectorServer = DefaultTrafficSelector.builder()
241 .matchEthType(Ethernet.TYPE_ARP);
242 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
243 }
244
245 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700246 public Map<HostId, IpAssignment> listMapping() {
samanwita pal0bff4302015-09-15 13:37:00 -0700247 return dhcpStore.listAssignedMapping();
samanwita palf28207b2015-09-04 10:41:56 -0700248 }
249
250 @Override
251 public int getLeaseTime() {
252 return leaseTime;
253 }
254
255 @Override
256 public int getRenewalTime() {
257 return renewalTime;
258 }
259
260 @Override
261 public int getRebindingTime() {
262 return rebindingTime;
263 }
264
265 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700266 public boolean setStaticMapping(MacAddress macAddress, IpAssignment ipAssignment) {
267 log.debug("setStaticMapping is called with Mac: {} IpAssignment: {}",
268 macAddress, ipAssignment);
269 return dhcpStore.assignStaticIP(macAddress, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700270 }
271
272 @Override
273 public boolean removeStaticMapping(MacAddress macID) {
274 return dhcpStore.removeStaticIP(macID);
275 }
276
277 @Override
278 public Iterable<Ip4Address> getAvailableIPs() {
279 return dhcpStore.getAvailableIPs();
280 }
281
Jonathan Hartb35540a2015-11-17 09:30:56 -0800282 private class DhcpPacketProcessor implements PacketProcessor {
samanwita palf28207b2015-09-04 10:41:56 -0700283
284 /**
285 * Builds the DHCP Reply packet.
286 *
287 * @param packet the incoming Ethernet frame
288 * @param ipOffered the IP offered by the DHCP Server
289 * @param outgoingMessageType the message type of the outgoing packet
290 * @return the Ethernet reply frame
291 */
samanwita pal2a313402015-09-14 16:03:22 -0700292 private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
samanwita palf28207b2015-09-04 10:41:56 -0700293
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700294 // mandatory options
295 // TODO save and get the information below to/from IP assignment
296 Ip4Address dhcpServerReply = myIP;
297 Ip4Address subnetMaskReply = subnetMask;
298 Ip4Address broadcastReply = broadcastAddress;
danielcd9deed2015-10-30 17:16:16 +0900299
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700300 // optional options
301 Optional<Ip4Address> routerAddressReply = Optional.of(routerAddress);
302 Optional<Ip4Address> domainServerReply = Optional.of(domainServer);
danielcd9deed2015-10-30 17:16:16 +0900303
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700304 IpAssignment ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(
305 HostId.hostId(packet.getSourceMAC()));
306
307 if (ipAssignment != null &&
308 ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900309 subnetMaskReply = ipAssignment.subnetMask();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700310 broadcastReply = ipAssignment.broadcast();
311 routerAddressReply = Optional.ofNullable(ipAssignment.routerAddress());
312 domainServerReply = Optional.ofNullable(ipAssignment.domainServer());
danielcd9deed2015-10-30 17:16:16 +0900313 }
314
samanwita palf28207b2015-09-04 10:41:56 -0700315 // Ethernet Frame.
316 Ethernet ethReply = new Ethernet();
317 ethReply.setSourceMACAddress(myMAC);
318 ethReply.setDestinationMACAddress(packet.getSourceMAC());
319 ethReply.setEtherType(Ethernet.TYPE_IPV4);
320 ethReply.setVlanID(packet.getVlanID());
321
322 // IP Packet
323 IPv4 ipv4Packet = (IPv4) packet.getPayload();
324 IPv4 ipv4Reply = new IPv4();
danielcd9deed2015-10-30 17:16:16 +0900325 ipv4Reply.setSourceAddress(dhcpServerReply.toInt());
samanwita pal2a313402015-09-14 16:03:22 -0700326 ipv4Reply.setDestinationAddress(ipOffered.toInt());
samanwita palf28207b2015-09-04 10:41:56 -0700327 ipv4Reply.setTtl(packetTTL);
328
329 // UDP Datagram.
330 UDP udpPacket = (UDP) ipv4Packet.getPayload();
331 UDP udpReply = new UDP();
332 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
333 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
334
335 // DHCP Payload.
336 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
337 DHCP dhcpReply = new DHCP();
338 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700339 dhcpReply.setFlags(dhcpPacket.getFlags());
340 dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
samanwita palf28207b2015-09-04 10:41:56 -0700341 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700342 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
343
Yi Tsengc7403c22017-06-19 16:23:22 -0700344 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700345 dhcpReply.setYourIPAddress(ipOffered.toInt());
danielcd9deed2015-10-30 17:16:16 +0900346 dhcpReply.setServerIPAddress(dhcpServerReply.toInt());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700347 if (dhcpPacket.getGatewayIPAddress() == 0) {
348 ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
349 }
350 }
samanwita palf28207b2015-09-04 10:41:56 -0700351 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
352 dhcpReply.setHardwareAddressLength((byte) 6);
353
354 // DHCP Options.
Yi Tsengc7403c22017-06-19 16:23:22 -0700355 DhcpOption option = new DhcpOption();
356 List<DhcpOption> optionList = new ArrayList<>();
samanwita palf28207b2015-09-04 10:41:56 -0700357
358 // DHCP Message Type.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700359 option.setCode(OptionCode_MessageType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700360 option.setLength((byte) 1);
361 byte[] optionData = {outgoingMessageType};
362 option.setData(optionData);
363 optionList.add(option);
364
365 // DHCP Server Identifier.
Yi Tsengc7403c22017-06-19 16:23:22 -0700366 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700367 option.setCode(OptionCode_DHCPServerIp.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700368 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900369 option.setData(dhcpServerReply.toOctets());
samanwita palf28207b2015-09-04 10:41:56 -0700370 optionList.add(option);
371
Yi Tsengc7403c22017-06-19 16:23:22 -0700372 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700373 // IP Address Lease Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700374 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700375 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
376 option.setLength((byte) 4);
daniel02f3af02015-12-17 18:44:52 +0900377 option.setData(ByteBuffer.allocate(4)
378 .putInt(ipAssignment == null ? leaseTime : ipAssignment.leasePeriod()).array());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700379 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700380
samanwita pala3d1d1c2015-09-25 11:50:15 -0700381 // IP Address Renewal Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700382 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700383 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
384 option.setLength((byte) 4);
385 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
386 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700387
samanwita pala3d1d1c2015-09-25 11:50:15 -0700388 // IP Address Rebinding Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700389 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700390 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
391 option.setLength((byte) 4);
392 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
393 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700394
samanwita pala3d1d1c2015-09-25 11:50:15 -0700395 // Subnet Mask.
Yi Tsengc7403c22017-06-19 16:23:22 -0700396 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700397 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
398 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900399 option.setData(subnetMaskReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700400 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700401
samanwita pala3d1d1c2015-09-25 11:50:15 -0700402 // Broadcast Address.
Yi Tsengc7403c22017-06-19 16:23:22 -0700403 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700404 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
405 option.setLength((byte) 4);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700406 option.setData(broadcastReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700407 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700408
samanwita pala3d1d1c2015-09-25 11:50:15 -0700409 // Router Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700410 if (routerAddressReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700411 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700412 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
413 option.setLength((byte) 4);
414 option.setData(routerAddressReply.get().toOctets());
415 optionList.add(option);
416 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700417
418 // DNS Server Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700419 if (domainServerReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700420 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700421 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
422 option.setLength((byte) 4);
423 option.setData(domainServerReply.get().toOctets());
424 optionList.add(option);
425 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700426 }
samanwita palf28207b2015-09-04 10:41:56 -0700427
428 // End Option.
Yi Tsengc7403c22017-06-19 16:23:22 -0700429 option = new DhcpOption();
samanwita palf28207b2015-09-04 10:41:56 -0700430 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
431 option.setLength((byte) 1);
432 optionList.add(option);
433
434 dhcpReply.setOptions(optionList);
samanwita palf28207b2015-09-04 10:41:56 -0700435 udpReply.setPayload(dhcpReply);
436 ipv4Reply.setPayload(udpReply);
437 ethReply.setPayload(ipv4Reply);
438
439 return ethReply;
440 }
441
442 /**
443 * Sends the Ethernet reply frame via the Packet Service.
444 *
445 * @param context the context of the incoming frame
446 * @param reply the Ethernet reply frame
447 */
448 private void sendReply(PacketContext context, Ethernet reply) {
449 if (reply != null) {
450 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
451 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
452 builder.setOutput(sourcePoint.port());
Thomas Vachuska00090442015-09-11 18:08:04 -0700453 context.block();
samanwita palf28207b2015-09-04 10:41:56 -0700454 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
samanwita pal8969cbe2015-09-04 13:31:30 -0700455 builder.build(), ByteBuffer.wrap(reply.serialize())));
samanwita palf28207b2015-09-04 10:41:56 -0700456 }
457 }
458
459 /**
460 * Processes the DHCP Payload and initiates a reply to the client.
461 *
462 * @param context context of the incoming message
463 * @param dhcpPayload the extracted DHCP payload
464 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800465 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700466 if (dhcpPayload == null) {
467 log.debug("DHCP packet without payload, do nothing");
468 return;
469 }
470
samanwita palf28207b2015-09-04 10:41:56 -0700471 Ethernet packet = context.inPacket().parsed();
Yi Tsengc7403c22017-06-19 16:23:22 -0700472 DHCP.MsgType incomingPacketType = null;
samanwita palf28207b2015-09-04 10:41:56 -0700473 boolean flagIfRequestedIP = false;
474 boolean flagIfServerIP = false;
475 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
476 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
477
Yi Tsengc7403c22017-06-19 16:23:22 -0700478 for (DhcpOption option : dhcpPayload.getOptions()) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700479 if (option.getCode() == OptionCode_MessageType.getValue()) {
480 byte[] data = option.getData();
Yi Tsengc7403c22017-06-19 16:23:22 -0700481 incomingPacketType = DHCP.MsgType.getType(data[0]);
samanwita palf28207b2015-09-04 10:41:56 -0700482 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700483 if (option.getCode() == OptionCode_RequestedIP.getValue()) {
484 byte[] data = option.getData();
485 requestedIP = Ip4Address.valueOf(data);
486 flagIfRequestedIP = true;
487 }
488 if (option.getCode() == OptionCode_DHCPServerIp.getValue()) {
489 byte[] data = option.getData();
490 serverIP = Ip4Address.valueOf(data);
491 flagIfServerIP = true;
492 }
493 }
samanwita palf28207b2015-09-04 10:41:56 -0700494
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700495 if (incomingPacketType == null) {
496 log.debug("No incoming packet type specified, ignore it");
497 return;
498 }
samanwita palf28207b2015-09-04 10:41:56 -0700499
Yi Tsengc7403c22017-06-19 16:23:22 -0700500 DHCP.MsgType outgoingPacketType;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700501 MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
502 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
503 HostId hostId = HostId.hostId(clientMac, vlanId);
danielcd9deed2015-10-30 17:16:16 +0900504
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700505 switch (incomingPacketType) {
506 case DHCPDISCOVER:
507 log.trace("DHCP DISCOVER received from {}", hostId);
508 Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
samanwita pal2a313402015-09-14 16:03:22 -0700509 if (ipOffered != null) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700510 Ethernet ethReply = buildReply(
511 packet,
512 ipOffered,
Yi Tsengc7403c22017-06-19 16:23:22 -0700513 (byte) DHCP.MsgType.DHCPOFFER.getValue());
samanwita pal2a313402015-09-14 16:03:22 -0700514 sendReply(context, ethReply);
515 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700516 break;
517 case DHCPREQUEST:
518 log.trace("DHCP REQUEST received from {}", hostId);
519 if (flagIfServerIP && !myIP.equals(serverIP)) {
520 return;
521 }
samanwita palf28207b2015-09-04 10:41:56 -0700522
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700523 if (!flagIfRequestedIP) {
524 // this is renew or rebinding request
525 int clientIp = dhcpPayload.getClientIPAddress();
526 requestedIP = Ip4Address.valueOf(clientIp);
527 }
samanwita palf28207b2015-09-04 10:41:56 -0700528
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700529 IpAssignment ipAssignment = IpAssignment.builder()
530 .ipAddress(requestedIP)
531 .leasePeriod(leaseTime)
532 .timestamp(new Date())
533 .assignmentStatus(Option_Requested).build();
daniel877bb2f2015-11-12 21:33:05 +0900534
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700535 if (dhcpStore.assignIP(hostId, ipAssignment)) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700536 outgoingPacketType = DHCP.MsgType.DHCPACK;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700537 discoverHost(context, requestedIP);
samanwita palf28207b2015-09-04 10:41:56 -0700538 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700539 outgoingPacketType = DHCP.MsgType.DHCPNAK;
samanwita palf28207b2015-09-04 10:41:56 -0700540 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700541
542 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
543 sendReply(context, ethReply);
544 break;
545 case DHCPRELEASE:
546 log.trace("DHCP RELEASE received from {}", hostId);
547 Ip4Address releaseIp = dhcpStore.releaseIP(hostId);
548 if (releaseIp != null) {
549 hostProviderService.removeIpFromHost(hostId, releaseIp);
samanwita palc40e5ed2015-09-24 11:01:51 -0700550 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700551 break;
552 default:
553 break;
samanwita palf28207b2015-09-04 10:41:56 -0700554 }
555 }
556
557 /**
558 * Processes the ARP Payload and initiates a reply to the client.
559 *
560 * @param context context of the incoming message
561 * @param packet the ethernet payload
562 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800563 private void processArpPacket(PacketContext context, Ethernet packet) {
samanwita palf28207b2015-09-04 10:41:56 -0700564
565 ARP arpPacket = (ARP) packet.getPayload();
566
567 ARP arpReply = (ARP) arpPacket.clone();
568 arpReply.setOpCode(ARP.OP_REPLY);
569
570 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
571 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
572 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
573 arpReply.setSenderHardwareAddress(myMAC.toBytes());
574
575 // Ethernet Frame.
576 Ethernet ethReply = new Ethernet();
577 ethReply.setSourceMACAddress(myMAC);
578 ethReply.setDestinationMACAddress(packet.getSourceMAC());
579 ethReply.setEtherType(Ethernet.TYPE_ARP);
580 ethReply.setVlanID(packet.getVlanID());
581
582 ethReply.setPayload(arpReply);
583 sendReply(context, ethReply);
584 }
585
586 /**
587 * Integrates hosts learned through DHCP into topology.
588 * @param context context of the incoming message
589 * @param ipAssigned IP Address assigned to the host by DHCP Manager
590 */
591 private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700592 if (!allowHostDiscovery) {
593 // host discovery is not allowed, do nothing
594 return;
595 }
596
samanwita palf28207b2015-09-04 10:41:56 -0700597 Ethernet packet = context.inPacket().parsed();
598 MacAddress mac = packet.getSourceMAC();
599 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
600 HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0);
601
602 Set<IpAddress> ips = new HashSet<>();
603 ips.add(ipAssigned);
604
605 HostId hostId = HostId.hostId(mac, vlanId);
606 DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700607
608 log.info("Discovered host {}", desc);
Ray Milkeydc083442016-02-22 11:27:57 -0800609 hostProviderService.hostDetected(hostId, desc, false);
samanwita palf28207b2015-09-04 10:41:56 -0700610 }
611
612
613 @Override
614 public void process(PacketContext context) {
samanwita palf28207b2015-09-04 10:41:56 -0700615 Ethernet packet = context.inPacket().parsed();
616 if (packet == null) {
617 return;
618 }
619
620 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
621 IPv4 ipv4Packet = (IPv4) packet.getPayload();
622
623 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
624 UDP udpPacket = (UDP) ipv4Packet.getPayload();
625
626 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
627 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
628 // This is meant for the dhcp server so process the packet here.
629
630 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Jonathan Hartb35540a2015-11-17 09:30:56 -0800631 processDhcpPacket(context, dhcpPayload);
samanwita palf28207b2015-09-04 10:41:56 -0700632 }
633 }
634 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
635 ARP arpPacket = (ARP) packet.getPayload();
636
637 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700638 Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
samanwita palf28207b2015-09-04 10:41:56 -0700639
Jonathan Hartb35540a2015-11-17 09:30:56 -0800640 processArpPacket(context, packet);
samanwita palf28207b2015-09-04 10:41:56 -0700641
642 }
643 }
644 }
645 }
646
647 private class InternalConfigListener implements NetworkConfigListener {
648
649 /**
650 * Reconfigures the DHCP Server according to the configuration parameters passed.
651 *
652 * @param cfg configuration object
653 */
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700654 private void reconfigureNetwork(DhcpConfig cfg) {
Thomas Vachuska00090442015-09-11 18:08:04 -0700655 if (cfg == null) {
656 return;
657 }
samanwita palf28207b2015-09-04 10:41:56 -0700658 if (cfg.ip() != null) {
659 myIP = cfg.ip();
660 }
661 if (cfg.mac() != null) {
samanwita pal2a313402015-09-14 16:03:22 -0700662 myMAC = cfg.mac();
samanwita palf28207b2015-09-04 10:41:56 -0700663 }
664 if (cfg.subnetMask() != null) {
665 subnetMask = cfg.subnetMask();
666 }
667 if (cfg.broadcastAddress() != null) {
668 broadcastAddress = cfg.broadcastAddress();
669 }
670 if (cfg.routerAddress() != null) {
671 routerAddress = cfg.routerAddress();
672 }
673 if (cfg.domainServer() != null) {
674 domainServer = cfg.domainServer();
675 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700676 if (cfg.ttl() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700677 packetTTL = (byte) cfg.ttl();
samanwita palf28207b2015-09-04 10:41:56 -0700678 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700679 if (cfg.leaseTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700680 leaseTime = cfg.leaseTime();
samanwita palf28207b2015-09-04 10:41:56 -0700681 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700682 if (cfg.renewTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700683 renewalTime = cfg.renewTime();
samanwita palf28207b2015-09-04 10:41:56 -0700684 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700685 if (cfg.rebindTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700686 rebindingTime = cfg.rebindTime();
687 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700688 if (cfg.defaultTimeout() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700689 dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout());
690 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700691 if (cfg.timerDelay() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700692 timerDelay = cfg.timerDelay();
693 }
694 if ((cfg.startIp() != null) && (cfg.endIp() != null)) {
695 dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp());
samanwita palf28207b2015-09-04 10:41:56 -0700696 }
697 }
698
samanwita palf28207b2015-09-04 10:41:56 -0700699
700 @Override
701 public void event(NetworkConfigEvent event) {
702
703 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
samanwita pal2a313402015-09-14 16:03:22 -0700704 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
705 event.configClass().equals(DhcpConfig.class)) {
706
707 DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class);
708 reconfigureNetwork(cfg);
709 log.info("Reconfigured");
samanwita palf28207b2015-09-04 10:41:56 -0700710 }
711 }
712 }
713
714 private class InternalHostProvider extends AbstractProvider implements HostProvider {
715
716 /**
717 * Creates a provider with the supplier identifier.
718 */
719 protected InternalHostProvider() {
720 super(PID);
721 }
722
723 @Override
724 public void triggerProbe(Host host) {
725 // nothing to do
726 }
727 }
samanwita pal2a313402015-09-14 16:03:22 -0700728
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700729 private class PurgeListTask implements Runnable {
samanwita pal2a313402015-09-14 16:03:22 -0700730
731 @Override
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700732 public void run() {
samanwita pal2a313402015-09-14 16:03:22 -0700733 IpAssignment ipAssignment;
734 Date dateNow = new Date();
735
samanwita pal0bff4302015-09-15 13:37:00 -0700736 Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping();
samanwita pal2a313402015-09-14 16:03:22 -0700737 for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) {
738 ipAssignment = entry.getValue();
739
740 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
741 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
742 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
743
samanwita palc40e5ed2015-09-24 11:01:51 -0700744 Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
745 if (ip4Address != null) {
746 hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
747 }
samanwita pal2a313402015-09-14 16:03:22 -0700748 }
749 }
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700750 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita pal2a313402015-09-14 16:03:22 -0700751 }
752 }
Jonathan Hartb35540a2015-11-17 09:30:56 -0800753}