blob: 7190aea479d480ba5d836bdbbe9d7697f5668b84 [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;
Yi Tsengc7403c22017-06-19 16:23:22 -070031import org.onlab.packet.dhcp.DhcpOption;
samanwita palf28207b2015-09-04 10:41:56 -070032import org.onlab.packet.Ethernet;
33import org.onlab.packet.IPv4;
34import org.onlab.packet.Ip4Address;
35import org.onlab.packet.IpAddress;
36import org.onlab.packet.MacAddress;
37import org.onlab.packet.TpPort;
38import org.onlab.packet.UDP;
39import org.onlab.packet.VlanId;
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;
80import java.util.List;
81import java.util.Map;
samanwita pal7ccc2bc2015-09-14 19:53:15 -070082import java.util.Objects;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070083import java.util.Optional;
samanwita palf28207b2015-09-04 10:41:56 -070084import java.util.Set;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070085import java.util.concurrent.ScheduledFuture;
samanwita pal2a313402015-09-14 16:03:22 -070086import java.util.concurrent.TimeUnit;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070087
88import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
89import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
90import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RequestedIP;
samanwita palf28207b2015-09-04 10:41:56 -070091import static org.onlab.packet.MacAddress.valueOf;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070092import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
93import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Requested;
samanwita palf28207b2015-09-04 10:41:56 -070094import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
95
96/**
97 * Skeletal ONOS DHCP Server application.
98 */
99@Component(immediate = true)
100@Service
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700101public class DhcpManager implements DhcpService {
samanwita palf28207b2015-09-04 10:41:56 -0700102
103 private static final ProviderId PID = new ProviderId("of", "org.onosproject.dhcp", true);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700104 private static final String ALLOW_HOST_DISCOVERY = "allowHostDiscovery";
105 private static final boolean DEFAULT_ALLOW_HOST_DISCOVERY = false;
106
samanwita palf28207b2015-09-04 10:41:56 -0700107 private final Logger log = LoggerFactory.getLogger(getClass());
108
Thomas Vachuska00090442015-09-11 18:08:04 -0700109 private final InternalConfigListener cfgListener = new InternalConfigListener();
samanwita palf28207b2015-09-04 10:41:56 -0700110
111 private final Set<ConfigFactory> factories = ImmutableSet.of(
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700112 new ConfigFactory<ApplicationId, DhcpConfig>(APP_SUBJECT_FACTORY,
113 DhcpConfig.class,
samanwita palf28207b2015-09-04 10:41:56 -0700114 "dhcp") {
115 @Override
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700116 public DhcpConfig createConfig() {
117 return new DhcpConfig();
samanwita palf28207b2015-09-04 10:41:56 -0700118 }
samanwita palf28207b2015-09-04 10:41:56 -0700119 }
120 );
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected NetworkConfigRegistry cfgService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected PacketService packetService;
126
Jonathan Hartb35540a2015-11-17 09:30:56 -0800127 private DhcpPacketProcessor processor = new DhcpPacketProcessor();
samanwita palf28207b2015-09-04 10:41:56 -0700128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected CoreService coreService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700133 protected DhcpStore dhcpStore;
samanwita palf28207b2015-09-04 10:41:56 -0700134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected HostProviderRegistry hostProviderRegistry;
137
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected ComponentConfigService componentConfigService;
140
141 @Property(name = ALLOW_HOST_DISCOVERY, boolValue = DEFAULT_ALLOW_HOST_DISCOVERY,
142 label = "Allow host discovery from DHCP request")
143 private boolean allowHostDiscovery = DEFAULT_ALLOW_HOST_DISCOVERY;
144
samanwita palf28207b2015-09-04 10:41:56 -0700145 protected HostProviderService hostProviderService;
samanwita pal2a313402015-09-14 16:03:22 -0700146 private final HostProvider hostProvider = new InternalHostProvider();
samanwita palf28207b2015-09-04 10:41:56 -0700147 private ApplicationId appId;
148
149 // Hardcoded values are default values.
samanwita palf28207b2015-09-04 10:41:56 -0700150 /**
151 * leaseTime - 10 mins or 600s.
152 * renewalTime - 5 mins or 300s.
153 * rebindingTime - 6 mins or 360s.
154 */
samanwita palf28207b2015-09-04 10:41:56 -0700155 private static int leaseTime = 600;
samanwita palf28207b2015-09-04 10:41:56 -0700156 private static int renewalTime = 300;
samanwita palf28207b2015-09-04 10:41:56 -0700157 private static int rebindingTime = 360;
samanwita palf28207b2015-09-04 10:41:56 -0700158 private static byte packetTTL = (byte) 127;
samanwita pal2a313402015-09-14 16:03:22 -0700159 private static Ip4Address subnetMask = Ip4Address.valueOf("255.0.0.0");
samanwita pal2a313402015-09-14 16:03:22 -0700160 private static Ip4Address broadcastAddress = Ip4Address.valueOf("10.255.255.255");
samanwita pal2a313402015-09-14 16:03:22 -0700161 private static Ip4Address routerAddress = Ip4Address.valueOf("10.0.0.2");
samanwita pal2a313402015-09-14 16:03:22 -0700162 private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700163 private static Ip4Address myIP = Ip4Address.valueOf("10.0.0.2");
Rafal Szalecki9638ef62017-02-14 15:05:51 +0100164 private static MacAddress myMAC = valueOf("4e:4f:4f:4f:4f:4f");
samanwita pala3d1d1c2015-09-25 11:50:15 -0700165 private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
166
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700167 protected ScheduledFuture<?> timeout;
samanwita pal2a313402015-09-14 16:03:22 -0700168 protected static int timerDelay = 2;
samanwita palf28207b2015-09-04 10:41:56 -0700169
170 @Activate
171 protected void activate() {
172 // start the dhcp server
173 appId = coreService.registerApplication("org.onosproject.dhcp");
174
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700175 componentConfigService.registerProperties(getClass());
samanwita palf28207b2015-09-04 10:41:56 -0700176 cfgService.addListener(cfgListener);
177 factories.forEach(cfgService::registerConfigFactory);
Thomas Vachuska00090442015-09-11 18:08:04 -0700178 cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class));
samanwita palf28207b2015-09-04 10:41:56 -0700179 hostProviderService = hostProviderRegistry.register(hostProvider);
Hyunsun Moon4f47a862016-08-18 11:30:59 -0700180 packetService.addProcessor(processor, PacketProcessor.director(1));
samanwita palf28207b2015-09-04 10:41:56 -0700181 requestPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700182 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita palf28207b2015-09-04 10:41:56 -0700183 log.info("Started");
184 }
185
186 @Deactivate
187 protected void deactivate() {
188 cfgService.removeListener(cfgListener);
189 factories.forEach(cfgService::unregisterConfigFactory);
190 packetService.removeProcessor(processor);
191 hostProviderRegistry.unregister(hostProvider);
192 hostProviderService = null;
193 cancelPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700194 timeout.cancel(true);
samanwita palf28207b2015-09-04 10:41:56 -0700195 log.info("Stopped");
196 }
197
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700198 @Modified
199 protected void modified(ComponentContext context) {
200 Dictionary<?, ?> properties = context.getProperties();
201
202 String updatedConfig = Tools.get(properties, ALLOW_HOST_DISCOVERY);
203 if (!Strings.isNullOrEmpty(updatedConfig)) {
204 allowHostDiscovery = Boolean.valueOf(updatedConfig);
205 log.info("Host discovery is set to {}", updatedConfig);
206 }
207
208 log.info("Modified");
209 }
210
samanwita palf28207b2015-09-04 10:41:56 -0700211 /**
212 * Request packet in via PacketService.
213 */
214 private void requestPackets() {
215
216 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
217 .matchEthType(Ethernet.TYPE_IPV4)
218 .matchIPProtocol(IPv4.PROTOCOL_UDP)
219 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
220 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
221 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
222
223 selectorServer = DefaultTrafficSelector.builder()
224 .matchEthType(Ethernet.TYPE_ARP);
225 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
226 }
227
228 /**
229 * Cancel requested packets in via packet service.
230 */
231 private void cancelPackets() {
232 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
233 .matchEthType(Ethernet.TYPE_IPV4)
234 .matchIPProtocol(IPv4.PROTOCOL_UDP)
235 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
236 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
237 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
238
239 selectorServer = DefaultTrafficSelector.builder()
240 .matchEthType(Ethernet.TYPE_ARP);
241 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
242 }
243
244 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700245 public Map<HostId, IpAssignment> listMapping() {
samanwita pal0bff4302015-09-15 13:37:00 -0700246 return dhcpStore.listAssignedMapping();
samanwita palf28207b2015-09-04 10:41:56 -0700247 }
248
249 @Override
250 public int getLeaseTime() {
251 return leaseTime;
252 }
253
254 @Override
255 public int getRenewalTime() {
256 return renewalTime;
257 }
258
259 @Override
260 public int getRebindingTime() {
261 return rebindingTime;
262 }
263
264 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700265 public boolean setStaticMapping(MacAddress macAddress, IpAssignment ipAssignment) {
266 log.debug("setStaticMapping is called with Mac: {} IpAssignment: {}",
267 macAddress, ipAssignment);
268 return dhcpStore.assignStaticIP(macAddress, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700269 }
270
271 @Override
272 public boolean removeStaticMapping(MacAddress macID) {
273 return dhcpStore.removeStaticIP(macID);
274 }
275
276 @Override
277 public Iterable<Ip4Address> getAvailableIPs() {
278 return dhcpStore.getAvailableIPs();
279 }
280
Jonathan Hartb35540a2015-11-17 09:30:56 -0800281 private class DhcpPacketProcessor implements PacketProcessor {
samanwita palf28207b2015-09-04 10:41:56 -0700282
283 /**
284 * Builds the DHCP Reply packet.
285 *
286 * @param packet the incoming Ethernet frame
287 * @param ipOffered the IP offered by the DHCP Server
288 * @param outgoingMessageType the message type of the outgoing packet
289 * @return the Ethernet reply frame
290 */
samanwita pal2a313402015-09-14 16:03:22 -0700291 private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
samanwita palf28207b2015-09-04 10:41:56 -0700292
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700293 // mandatory options
294 // TODO save and get the information below to/from IP assignment
295 Ip4Address dhcpServerReply = myIP;
296 Ip4Address subnetMaskReply = subnetMask;
297 Ip4Address broadcastReply = broadcastAddress;
danielcd9deed2015-10-30 17:16:16 +0900298
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700299 // optional options
300 Optional<Ip4Address> routerAddressReply = Optional.of(routerAddress);
301 Optional<Ip4Address> domainServerReply = Optional.of(domainServer);
danielcd9deed2015-10-30 17:16:16 +0900302
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700303 IpAssignment ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(
304 HostId.hostId(packet.getSourceMAC()));
305
306 if (ipAssignment != null &&
307 ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900308 subnetMaskReply = ipAssignment.subnetMask();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700309 broadcastReply = ipAssignment.broadcast();
310 routerAddressReply = Optional.ofNullable(ipAssignment.routerAddress());
311 domainServerReply = Optional.ofNullable(ipAssignment.domainServer());
danielcd9deed2015-10-30 17:16:16 +0900312 }
313
samanwita palf28207b2015-09-04 10:41:56 -0700314 // Ethernet Frame.
315 Ethernet ethReply = new Ethernet();
316 ethReply.setSourceMACAddress(myMAC);
317 ethReply.setDestinationMACAddress(packet.getSourceMAC());
318 ethReply.setEtherType(Ethernet.TYPE_IPV4);
319 ethReply.setVlanID(packet.getVlanID());
320
321 // IP Packet
322 IPv4 ipv4Packet = (IPv4) packet.getPayload();
323 IPv4 ipv4Reply = new IPv4();
danielcd9deed2015-10-30 17:16:16 +0900324 ipv4Reply.setSourceAddress(dhcpServerReply.toInt());
samanwita pal2a313402015-09-14 16:03:22 -0700325 ipv4Reply.setDestinationAddress(ipOffered.toInt());
samanwita palf28207b2015-09-04 10:41:56 -0700326 ipv4Reply.setTtl(packetTTL);
327
328 // UDP Datagram.
329 UDP udpPacket = (UDP) ipv4Packet.getPayload();
330 UDP udpReply = new UDP();
331 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
332 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
333
334 // DHCP Payload.
335 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
336 DHCP dhcpReply = new DHCP();
337 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700338 dhcpReply.setFlags(dhcpPacket.getFlags());
339 dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
samanwita palf28207b2015-09-04 10:41:56 -0700340 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700341 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
342
Yi Tsengc7403c22017-06-19 16:23:22 -0700343 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700344 dhcpReply.setYourIPAddress(ipOffered.toInt());
danielcd9deed2015-10-30 17:16:16 +0900345 dhcpReply.setServerIPAddress(dhcpServerReply.toInt());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700346 if (dhcpPacket.getGatewayIPAddress() == 0) {
347 ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
348 }
349 }
samanwita palf28207b2015-09-04 10:41:56 -0700350 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
351 dhcpReply.setHardwareAddressLength((byte) 6);
352
353 // DHCP Options.
Yi Tsengc7403c22017-06-19 16:23:22 -0700354 DhcpOption option = new DhcpOption();
355 List<DhcpOption> optionList = new ArrayList<>();
samanwita palf28207b2015-09-04 10:41:56 -0700356
357 // DHCP Message Type.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700358 option.setCode(OptionCode_MessageType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700359 option.setLength((byte) 1);
360 byte[] optionData = {outgoingMessageType};
361 option.setData(optionData);
362 optionList.add(option);
363
364 // DHCP Server Identifier.
Yi Tsengc7403c22017-06-19 16:23:22 -0700365 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700366 option.setCode(OptionCode_DHCPServerIp.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700367 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900368 option.setData(dhcpServerReply.toOctets());
samanwita palf28207b2015-09-04 10:41:56 -0700369 optionList.add(option);
370
Yi Tsengc7403c22017-06-19 16:23:22 -0700371 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700372 // IP Address Lease Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700373 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700374 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
375 option.setLength((byte) 4);
daniel02f3af02015-12-17 18:44:52 +0900376 option.setData(ByteBuffer.allocate(4)
377 .putInt(ipAssignment == null ? leaseTime : ipAssignment.leasePeriod()).array());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700378 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700379
samanwita pala3d1d1c2015-09-25 11:50:15 -0700380 // IP Address Renewal Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700381 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700382 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
383 option.setLength((byte) 4);
384 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
385 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700386
samanwita pala3d1d1c2015-09-25 11:50:15 -0700387 // IP Address Rebinding Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700388 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700389 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
390 option.setLength((byte) 4);
391 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
392 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700393
samanwita pala3d1d1c2015-09-25 11:50:15 -0700394 // Subnet Mask.
Yi Tsengc7403c22017-06-19 16:23:22 -0700395 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700396 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
397 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900398 option.setData(subnetMaskReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700399 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700400
samanwita pala3d1d1c2015-09-25 11:50:15 -0700401 // Broadcast Address.
Yi Tsengc7403c22017-06-19 16:23:22 -0700402 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700403 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
404 option.setLength((byte) 4);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700405 option.setData(broadcastReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700406 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700407
samanwita pala3d1d1c2015-09-25 11:50:15 -0700408 // Router Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700409 if (routerAddressReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700410 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700411 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
412 option.setLength((byte) 4);
413 option.setData(routerAddressReply.get().toOctets());
414 optionList.add(option);
415 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700416
417 // DNS Server Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700418 if (domainServerReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700419 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700420 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
421 option.setLength((byte) 4);
422 option.setData(domainServerReply.get().toOctets());
423 optionList.add(option);
424 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700425 }
samanwita palf28207b2015-09-04 10:41:56 -0700426
427 // End Option.
Yi Tsengc7403c22017-06-19 16:23:22 -0700428 option = new DhcpOption();
samanwita palf28207b2015-09-04 10:41:56 -0700429 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
430 option.setLength((byte) 1);
431 optionList.add(option);
432
433 dhcpReply.setOptions(optionList);
samanwita palf28207b2015-09-04 10:41:56 -0700434 udpReply.setPayload(dhcpReply);
435 ipv4Reply.setPayload(udpReply);
436 ethReply.setPayload(ipv4Reply);
437
438 return ethReply;
439 }
440
441 /**
442 * Sends the Ethernet reply frame via the Packet Service.
443 *
444 * @param context the context of the incoming frame
445 * @param reply the Ethernet reply frame
446 */
447 private void sendReply(PacketContext context, Ethernet reply) {
448 if (reply != null) {
449 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
450 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
451 builder.setOutput(sourcePoint.port());
Thomas Vachuska00090442015-09-11 18:08:04 -0700452 context.block();
samanwita palf28207b2015-09-04 10:41:56 -0700453 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
samanwita pal8969cbe2015-09-04 13:31:30 -0700454 builder.build(), ByteBuffer.wrap(reply.serialize())));
samanwita palf28207b2015-09-04 10:41:56 -0700455 }
456 }
457
458 /**
459 * Processes the DHCP Payload and initiates a reply to the client.
460 *
461 * @param context context of the incoming message
462 * @param dhcpPayload the extracted DHCP payload
463 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800464 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700465 if (dhcpPayload == null) {
466 log.debug("DHCP packet without payload, do nothing");
467 return;
468 }
469
samanwita palf28207b2015-09-04 10:41:56 -0700470 Ethernet packet = context.inPacket().parsed();
Yi Tsengc7403c22017-06-19 16:23:22 -0700471 DHCP.MsgType incomingPacketType = null;
samanwita palf28207b2015-09-04 10:41:56 -0700472 boolean flagIfRequestedIP = false;
473 boolean flagIfServerIP = false;
474 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
475 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
476
Yi Tsengc7403c22017-06-19 16:23:22 -0700477 for (DhcpOption option : dhcpPayload.getOptions()) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700478 if (option.getCode() == OptionCode_MessageType.getValue()) {
479 byte[] data = option.getData();
Yi Tsengc7403c22017-06-19 16:23:22 -0700480 incomingPacketType = DHCP.MsgType.getType(data[0]);
samanwita palf28207b2015-09-04 10:41:56 -0700481 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700482 if (option.getCode() == OptionCode_RequestedIP.getValue()) {
483 byte[] data = option.getData();
484 requestedIP = Ip4Address.valueOf(data);
485 flagIfRequestedIP = true;
486 }
487 if (option.getCode() == OptionCode_DHCPServerIp.getValue()) {
488 byte[] data = option.getData();
489 serverIP = Ip4Address.valueOf(data);
490 flagIfServerIP = true;
491 }
492 }
samanwita palf28207b2015-09-04 10:41:56 -0700493
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700494 if (incomingPacketType == null) {
495 log.debug("No incoming packet type specified, ignore it");
496 return;
497 }
samanwita palf28207b2015-09-04 10:41:56 -0700498
Yi Tsengc7403c22017-06-19 16:23:22 -0700499 DHCP.MsgType outgoingPacketType;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700500 MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
501 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
502 HostId hostId = HostId.hostId(clientMac, vlanId);
danielcd9deed2015-10-30 17:16:16 +0900503
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700504 switch (incomingPacketType) {
505 case DHCPDISCOVER:
506 log.trace("DHCP DISCOVER received from {}", hostId);
507 Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
samanwita pal2a313402015-09-14 16:03:22 -0700508 if (ipOffered != null) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700509 Ethernet ethReply = buildReply(
510 packet,
511 ipOffered,
Yi Tsengc7403c22017-06-19 16:23:22 -0700512 (byte) DHCP.MsgType.DHCPOFFER.getValue());
samanwita pal2a313402015-09-14 16:03:22 -0700513 sendReply(context, ethReply);
514 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700515 break;
516 case DHCPREQUEST:
517 log.trace("DHCP REQUEST received from {}", hostId);
518 if (flagIfServerIP && !myIP.equals(serverIP)) {
519 return;
520 }
samanwita palf28207b2015-09-04 10:41:56 -0700521
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700522 if (!flagIfRequestedIP) {
523 // this is renew or rebinding request
524 int clientIp = dhcpPayload.getClientIPAddress();
525 requestedIP = Ip4Address.valueOf(clientIp);
526 }
samanwita palf28207b2015-09-04 10:41:56 -0700527
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700528 IpAssignment ipAssignment = IpAssignment.builder()
529 .ipAddress(requestedIP)
530 .leasePeriod(leaseTime)
531 .timestamp(new Date())
532 .assignmentStatus(Option_Requested).build();
daniel877bb2f2015-11-12 21:33:05 +0900533
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700534 if (dhcpStore.assignIP(hostId, ipAssignment)) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700535 outgoingPacketType = DHCP.MsgType.DHCPACK;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700536 discoverHost(context, requestedIP);
samanwita palf28207b2015-09-04 10:41:56 -0700537 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700538 outgoingPacketType = DHCP.MsgType.DHCPNAK;
samanwita palf28207b2015-09-04 10:41:56 -0700539 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700540
541 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
542 sendReply(context, ethReply);
543 break;
544 case DHCPRELEASE:
545 log.trace("DHCP RELEASE received from {}", hostId);
546 Ip4Address releaseIp = dhcpStore.releaseIP(hostId);
547 if (releaseIp != null) {
548 hostProviderService.removeIpFromHost(hostId, releaseIp);
samanwita palc40e5ed2015-09-24 11:01:51 -0700549 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700550 break;
551 default:
552 break;
samanwita palf28207b2015-09-04 10:41:56 -0700553 }
554 }
555
556 /**
557 * Processes the ARP Payload and initiates a reply to the client.
558 *
559 * @param context context of the incoming message
560 * @param packet the ethernet payload
561 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800562 private void processArpPacket(PacketContext context, Ethernet packet) {
samanwita palf28207b2015-09-04 10:41:56 -0700563
564 ARP arpPacket = (ARP) packet.getPayload();
565
566 ARP arpReply = (ARP) arpPacket.clone();
567 arpReply.setOpCode(ARP.OP_REPLY);
568
569 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
570 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
571 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
572 arpReply.setSenderHardwareAddress(myMAC.toBytes());
573
574 // Ethernet Frame.
575 Ethernet ethReply = new Ethernet();
576 ethReply.setSourceMACAddress(myMAC);
577 ethReply.setDestinationMACAddress(packet.getSourceMAC());
578 ethReply.setEtherType(Ethernet.TYPE_ARP);
579 ethReply.setVlanID(packet.getVlanID());
580
581 ethReply.setPayload(arpReply);
582 sendReply(context, ethReply);
583 }
584
585 /**
586 * Integrates hosts learned through DHCP into topology.
587 * @param context context of the incoming message
588 * @param ipAssigned IP Address assigned to the host by DHCP Manager
589 */
590 private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700591 if (!allowHostDiscovery) {
592 // host discovery is not allowed, do nothing
593 return;
594 }
595
samanwita palf28207b2015-09-04 10:41:56 -0700596 Ethernet packet = context.inPacket().parsed();
597 MacAddress mac = packet.getSourceMAC();
598 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
599 HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0);
600
601 Set<IpAddress> ips = new HashSet<>();
602 ips.add(ipAssigned);
603
604 HostId hostId = HostId.hostId(mac, vlanId);
605 DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700606
607 log.info("Discovered host {}", desc);
Ray Milkeydc083442016-02-22 11:27:57 -0800608 hostProviderService.hostDetected(hostId, desc, false);
samanwita palf28207b2015-09-04 10:41:56 -0700609 }
610
611
612 @Override
613 public void process(PacketContext context) {
samanwita palf28207b2015-09-04 10:41:56 -0700614 Ethernet packet = context.inPacket().parsed();
615 if (packet == null) {
616 return;
617 }
618
619 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
620 IPv4 ipv4Packet = (IPv4) packet.getPayload();
621
622 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
623 UDP udpPacket = (UDP) ipv4Packet.getPayload();
624
625 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
626 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
627 // This is meant for the dhcp server so process the packet here.
628
629 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Jonathan Hartb35540a2015-11-17 09:30:56 -0800630 processDhcpPacket(context, dhcpPayload);
samanwita palf28207b2015-09-04 10:41:56 -0700631 }
632 }
633 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
634 ARP arpPacket = (ARP) packet.getPayload();
635
636 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700637 Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
samanwita palf28207b2015-09-04 10:41:56 -0700638
Jonathan Hartb35540a2015-11-17 09:30:56 -0800639 processArpPacket(context, packet);
samanwita palf28207b2015-09-04 10:41:56 -0700640
641 }
642 }
643 }
644 }
645
646 private class InternalConfigListener implements NetworkConfigListener {
647
648 /**
649 * Reconfigures the DHCP Server according to the configuration parameters passed.
650 *
651 * @param cfg configuration object
652 */
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700653 private void reconfigureNetwork(DhcpConfig cfg) {
Thomas Vachuska00090442015-09-11 18:08:04 -0700654 if (cfg == null) {
655 return;
656 }
samanwita palf28207b2015-09-04 10:41:56 -0700657 if (cfg.ip() != null) {
658 myIP = cfg.ip();
659 }
660 if (cfg.mac() != null) {
samanwita pal2a313402015-09-14 16:03:22 -0700661 myMAC = cfg.mac();
samanwita palf28207b2015-09-04 10:41:56 -0700662 }
663 if (cfg.subnetMask() != null) {
664 subnetMask = cfg.subnetMask();
665 }
666 if (cfg.broadcastAddress() != null) {
667 broadcastAddress = cfg.broadcastAddress();
668 }
669 if (cfg.routerAddress() != null) {
670 routerAddress = cfg.routerAddress();
671 }
672 if (cfg.domainServer() != null) {
673 domainServer = cfg.domainServer();
674 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700675 if (cfg.ttl() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700676 packetTTL = (byte) cfg.ttl();
samanwita palf28207b2015-09-04 10:41:56 -0700677 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700678 if (cfg.leaseTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700679 leaseTime = cfg.leaseTime();
samanwita palf28207b2015-09-04 10:41:56 -0700680 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700681 if (cfg.renewTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700682 renewalTime = cfg.renewTime();
samanwita palf28207b2015-09-04 10:41:56 -0700683 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700684 if (cfg.rebindTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700685 rebindingTime = cfg.rebindTime();
686 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700687 if (cfg.defaultTimeout() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700688 dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout());
689 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700690 if (cfg.timerDelay() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700691 timerDelay = cfg.timerDelay();
692 }
693 if ((cfg.startIp() != null) && (cfg.endIp() != null)) {
694 dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp());
samanwita palf28207b2015-09-04 10:41:56 -0700695 }
696 }
697
samanwita palf28207b2015-09-04 10:41:56 -0700698
699 @Override
700 public void event(NetworkConfigEvent event) {
701
702 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
samanwita pal2a313402015-09-14 16:03:22 -0700703 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
704 event.configClass().equals(DhcpConfig.class)) {
705
706 DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class);
707 reconfigureNetwork(cfg);
708 log.info("Reconfigured");
samanwita palf28207b2015-09-04 10:41:56 -0700709 }
710 }
711 }
712
713 private class InternalHostProvider extends AbstractProvider implements HostProvider {
714
715 /**
716 * Creates a provider with the supplier identifier.
717 */
718 protected InternalHostProvider() {
719 super(PID);
720 }
721
722 @Override
723 public void triggerProbe(Host host) {
724 // nothing to do
725 }
726 }
samanwita pal2a313402015-09-14 16:03:22 -0700727
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700728 private class PurgeListTask implements Runnable {
samanwita pal2a313402015-09-14 16:03:22 -0700729
730 @Override
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700731 public void run() {
samanwita pal2a313402015-09-14 16:03:22 -0700732 IpAssignment ipAssignment;
733 Date dateNow = new Date();
734
samanwita pal0bff4302015-09-15 13:37:00 -0700735 Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping();
samanwita pal2a313402015-09-14 16:03:22 -0700736 for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) {
737 ipAssignment = entry.getValue();
738
739 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
740 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
741 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
742
samanwita palc40e5ed2015-09-24 11:01:51 -0700743 Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
744 if (ip4Address != null) {
745 hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
746 }
samanwita pal2a313402015-09-14 16:03:22 -0700747 }
748 }
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700749 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita pal2a313402015-09-14 16:03:22 -0700750 }
751 }
Jonathan Hartb35540a2015-11-17 09:30:56 -0800752}