blob: a0f954857af2d91b718ae2f6e1c90d2503de531f [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;
samanwita palf28207b2015-09-04 10:41:56 -070020import org.onlab.packet.ARP;
21import org.onlab.packet.DHCP;
samanwita palf28207b2015-09-04 10:41:56 -070022import org.onlab.packet.Ethernet;
23import org.onlab.packet.IPv4;
24import org.onlab.packet.Ip4Address;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
27import org.onlab.packet.TpPort;
28import org.onlab.packet.UDP;
29import org.onlab.packet.VlanId;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070030import org.onlab.packet.dhcp.DhcpOption;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070031import org.onlab.util.SharedScheduledExecutors;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070032import org.onlab.util.Tools;
33import org.onosproject.cfg.ComponentConfigService;
samanwita palf28207b2015-09-04 10:41:56 -070034import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070036import org.onosproject.dhcp.DhcpService;
37import org.onosproject.dhcp.DhcpStore;
38import org.onosproject.dhcp.IpAssignment;
samanwita palf28207b2015-09-04 10:41:56 -070039import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.Host;
41import org.onosproject.net.HostId;
42import org.onosproject.net.HostLocation;
samanwita pal2a313402015-09-14 16:03:22 -070043import org.onosproject.net.config.ConfigFactory;
44import org.onosproject.net.config.NetworkConfigEvent;
45import org.onosproject.net.config.NetworkConfigListener;
46import org.onosproject.net.config.NetworkConfigRegistry;
samanwita palf28207b2015-09-04 10:41:56 -070047import org.onosproject.net.flow.DefaultTrafficSelector;
48import org.onosproject.net.flow.DefaultTrafficTreatment;
49import org.onosproject.net.flow.TrafficSelector;
50import org.onosproject.net.flow.TrafficTreatment;
51import org.onosproject.net.host.DefaultHostDescription;
52import org.onosproject.net.host.HostProvider;
53import org.onosproject.net.host.HostProviderRegistry;
54import org.onosproject.net.host.HostProviderService;
55import org.onosproject.net.packet.DefaultOutboundPacket;
56import org.onosproject.net.packet.PacketContext;
57import org.onosproject.net.packet.PacketPriority;
58import org.onosproject.net.packet.PacketProcessor;
59import org.onosproject.net.packet.PacketService;
60import org.onosproject.net.provider.AbstractProvider;
61import org.onosproject.net.provider.ProviderId;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070062import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070063import org.osgi.service.component.annotations.Activate;
64import org.osgi.service.component.annotations.Component;
65import org.osgi.service.component.annotations.Deactivate;
66import org.osgi.service.component.annotations.Modified;
67import org.osgi.service.component.annotations.Reference;
68import org.osgi.service.component.annotations.ReferenceCardinality;
samanwita palf28207b2015-09-04 10:41:56 -070069import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71
72import java.nio.ByteBuffer;
73import java.util.ArrayList;
samanwita pal2a313402015-09-14 16:03:22 -070074import java.util.Date;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070075import java.util.Dictionary;
samanwita palf28207b2015-09-04 10:41:56 -070076import java.util.HashSet;
77import java.util.List;
78import java.util.Map;
samanwita pal7ccc2bc2015-09-14 19:53:15 -070079import java.util.Objects;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070080import java.util.Optional;
samanwita palf28207b2015-09-04 10:41:56 -070081import java.util.Set;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070082import java.util.concurrent.ScheduledFuture;
samanwita pal2a313402015-09-14 16:03:22 -070083import java.util.concurrent.TimeUnit;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070084
85import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
86import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
87import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RequestedIP;
samanwita palf28207b2015-09-04 10:41:56 -070088import static org.onlab.packet.MacAddress.valueOf;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -070089import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
90import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Requested;
samanwita palf28207b2015-09-04 10:41:56 -070091import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
92
93/**
94 * Skeletal ONOS DHCP Server application.
95 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096@Component(immediate = true, service = DhcpService.class)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -070097public class DhcpManager implements DhcpService {
samanwita palf28207b2015-09-04 10:41:56 -070098
99 private static final ProviderId PID = new ProviderId("of", "org.onosproject.dhcp", true);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700100 private static final String ALLOW_HOST_DISCOVERY = "allowHostDiscovery";
101 private static final boolean DEFAULT_ALLOW_HOST_DISCOVERY = false;
102
samanwita palf28207b2015-09-04 10:41:56 -0700103 private final Logger log = LoggerFactory.getLogger(getClass());
104
Thomas Vachuska00090442015-09-11 18:08:04 -0700105 private final InternalConfigListener cfgListener = new InternalConfigListener();
samanwita palf28207b2015-09-04 10:41:56 -0700106
107 private final Set<ConfigFactory> factories = ImmutableSet.of(
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700108 new ConfigFactory<ApplicationId, DhcpConfig>(APP_SUBJECT_FACTORY,
109 DhcpConfig.class,
samanwita palf28207b2015-09-04 10:41:56 -0700110 "dhcp") {
111 @Override
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700112 public DhcpConfig createConfig() {
113 return new DhcpConfig();
samanwita palf28207b2015-09-04 10:41:56 -0700114 }
samanwita palf28207b2015-09-04 10:41:56 -0700115 }
116 );
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
samanwita palf28207b2015-09-04 10:41:56 -0700118 protected NetworkConfigRegistry cfgService;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
samanwita palf28207b2015-09-04 10:41:56 -0700121 protected PacketService packetService;
122
Jonathan Hartb35540a2015-11-17 09:30:56 -0800123 private DhcpPacketProcessor processor = new DhcpPacketProcessor();
samanwita palf28207b2015-09-04 10:41:56 -0700124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
samanwita palf28207b2015-09-04 10:41:56 -0700126 protected CoreService coreService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700129 protected DhcpStore dhcpStore;
samanwita palf28207b2015-09-04 10:41:56 -0700130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
samanwita palf28207b2015-09-04 10:41:56 -0700132 protected HostProviderRegistry hostProviderRegistry;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700135 protected ComponentConfigService componentConfigService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 //@Property(name = ALLOW_HOST_DISCOVERY, boolValue = DEFAULT_ALLOW_HOST_DISCOVERY,
138 // label = "Allow host discovery from DHCP request")
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700139 private boolean allowHostDiscovery = DEFAULT_ALLOW_HOST_DISCOVERY;
140
samanwita palf28207b2015-09-04 10:41:56 -0700141 protected HostProviderService hostProviderService;
samanwita pal2a313402015-09-14 16:03:22 -0700142 private final HostProvider hostProvider = new InternalHostProvider();
samanwita palf28207b2015-09-04 10:41:56 -0700143 private ApplicationId appId;
144
145 // Hardcoded values are default values.
samanwita palf28207b2015-09-04 10:41:56 -0700146 /**
147 * leaseTime - 10 mins or 600s.
148 * renewalTime - 5 mins or 300s.
149 * rebindingTime - 6 mins or 360s.
150 */
samanwita palf28207b2015-09-04 10:41:56 -0700151 private static int leaseTime = 600;
samanwita palf28207b2015-09-04 10:41:56 -0700152 private static int renewalTime = 300;
samanwita palf28207b2015-09-04 10:41:56 -0700153 private static int rebindingTime = 360;
samanwita palf28207b2015-09-04 10:41:56 -0700154 private static byte packetTTL = (byte) 127;
samanwita pal2a313402015-09-14 16:03:22 -0700155 private static Ip4Address subnetMask = Ip4Address.valueOf("255.0.0.0");
samanwita pal2a313402015-09-14 16:03:22 -0700156 private static Ip4Address broadcastAddress = Ip4Address.valueOf("10.255.255.255");
samanwita pal2a313402015-09-14 16:03:22 -0700157 private static Ip4Address routerAddress = Ip4Address.valueOf("10.0.0.2");
samanwita pal2a313402015-09-14 16:03:22 -0700158 private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700159 private static Ip4Address myIP = Ip4Address.valueOf("10.0.0.2");
Rafal Szalecki9638ef62017-02-14 15:05:51 +0100160 private static MacAddress myMAC = valueOf("4e:4f:4f:4f:4f:4f");
samanwita pala3d1d1c2015-09-25 11:50:15 -0700161 private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
162
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700163 protected ScheduledFuture<?> timeout;
samanwita pal2a313402015-09-14 16:03:22 -0700164 protected static int timerDelay = 2;
samanwita palf28207b2015-09-04 10:41:56 -0700165
166 @Activate
167 protected void activate() {
168 // start the dhcp server
169 appId = coreService.registerApplication("org.onosproject.dhcp");
170
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700171 componentConfigService.registerProperties(getClass());
samanwita palf28207b2015-09-04 10:41:56 -0700172 cfgService.addListener(cfgListener);
173 factories.forEach(cfgService::registerConfigFactory);
Thomas Vachuska00090442015-09-11 18:08:04 -0700174 cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class));
samanwita palf28207b2015-09-04 10:41:56 -0700175 hostProviderService = hostProviderRegistry.register(hostProvider);
Hyunsun Moon4f47a862016-08-18 11:30:59 -0700176 packetService.addProcessor(processor, PacketProcessor.director(1));
samanwita palf28207b2015-09-04 10:41:56 -0700177 requestPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700178 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita palf28207b2015-09-04 10:41:56 -0700179 log.info("Started");
180 }
181
182 @Deactivate
183 protected void deactivate() {
Thomas Vachuskafa615492018-03-19 09:11:51 -0700184 componentConfigService.unregisterProperties(getClass(), false);
samanwita palf28207b2015-09-04 10:41:56 -0700185 cfgService.removeListener(cfgListener);
186 factories.forEach(cfgService::unregisterConfigFactory);
187 packetService.removeProcessor(processor);
188 hostProviderRegistry.unregister(hostProvider);
189 hostProviderService = null;
190 cancelPackets();
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700191 timeout.cancel(true);
samanwita palf28207b2015-09-04 10:41:56 -0700192 log.info("Stopped");
193 }
194
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700195 @Modified
196 protected void modified(ComponentContext context) {
197 Dictionary<?, ?> properties = context.getProperties();
198
199 String updatedConfig = Tools.get(properties, ALLOW_HOST_DISCOVERY);
200 if (!Strings.isNullOrEmpty(updatedConfig)) {
201 allowHostDiscovery = Boolean.valueOf(updatedConfig);
202 log.info("Host discovery is set to {}", updatedConfig);
203 }
204
205 log.info("Modified");
206 }
207
samanwita palf28207b2015-09-04 10:41:56 -0700208 /**
209 * Request packet in via PacketService.
210 */
211 private void requestPackets() {
212
213 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
214 .matchEthType(Ethernet.TYPE_IPV4)
215 .matchIPProtocol(IPv4.PROTOCOL_UDP)
216 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
217 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
218 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
219
220 selectorServer = DefaultTrafficSelector.builder()
221 .matchEthType(Ethernet.TYPE_ARP);
222 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
223 }
224
225 /**
226 * Cancel requested packets in via packet service.
227 */
228 private void cancelPackets() {
229 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
230 .matchEthType(Ethernet.TYPE_IPV4)
231 .matchIPProtocol(IPv4.PROTOCOL_UDP)
232 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
233 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
234 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
235
236 selectorServer = DefaultTrafficSelector.builder()
237 .matchEthType(Ethernet.TYPE_ARP);
238 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
239 }
240
241 @Override
samanwita pal2a313402015-09-14 16:03:22 -0700242 public Map<HostId, IpAssignment> listMapping() {
samanwita pal0bff4302015-09-15 13:37:00 -0700243 return dhcpStore.listAssignedMapping();
samanwita palf28207b2015-09-04 10:41:56 -0700244 }
245
246 @Override
247 public int getLeaseTime() {
248 return leaseTime;
249 }
250
251 @Override
252 public int getRenewalTime() {
253 return renewalTime;
254 }
255
256 @Override
257 public int getRebindingTime() {
258 return rebindingTime;
259 }
260
261 @Override
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700262 public boolean setStaticMapping(MacAddress macAddress, IpAssignment ipAssignment) {
263 log.debug("setStaticMapping is called with Mac: {} IpAssignment: {}",
264 macAddress, ipAssignment);
265 return dhcpStore.assignStaticIP(macAddress, ipAssignment);
samanwita palf28207b2015-09-04 10:41:56 -0700266 }
267
268 @Override
269 public boolean removeStaticMapping(MacAddress macID) {
270 return dhcpStore.removeStaticIP(macID);
271 }
272
273 @Override
274 public Iterable<Ip4Address> getAvailableIPs() {
275 return dhcpStore.getAvailableIPs();
276 }
277
Jonathan Hartb35540a2015-11-17 09:30:56 -0800278 private class DhcpPacketProcessor implements PacketProcessor {
samanwita palf28207b2015-09-04 10:41:56 -0700279
280 /**
281 * Builds the DHCP Reply packet.
282 *
283 * @param packet the incoming Ethernet frame
284 * @param ipOffered the IP offered by the DHCP Server
285 * @param outgoingMessageType the message type of the outgoing packet
286 * @return the Ethernet reply frame
287 */
samanwita pal2a313402015-09-14 16:03:22 -0700288 private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
samanwita palf28207b2015-09-04 10:41:56 -0700289
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700290 // mandatory options
291 // TODO save and get the information below to/from IP assignment
292 Ip4Address dhcpServerReply = myIP;
293 Ip4Address subnetMaskReply = subnetMask;
294 Ip4Address broadcastReply = broadcastAddress;
danielcd9deed2015-10-30 17:16:16 +0900295
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700296 // optional options
297 Optional<Ip4Address> routerAddressReply = Optional.of(routerAddress);
298 Optional<Ip4Address> domainServerReply = Optional.of(domainServer);
danielcd9deed2015-10-30 17:16:16 +0900299
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700300 IpAssignment ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(
301 HostId.hostId(packet.getSourceMAC()));
302
303 if (ipAssignment != null &&
304 ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
danielcd9deed2015-10-30 17:16:16 +0900305 subnetMaskReply = ipAssignment.subnetMask();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700306 broadcastReply = ipAssignment.broadcast();
307 routerAddressReply = Optional.ofNullable(ipAssignment.routerAddress());
308 domainServerReply = Optional.ofNullable(ipAssignment.domainServer());
danielcd9deed2015-10-30 17:16:16 +0900309 }
310
samanwita palf28207b2015-09-04 10:41:56 -0700311 // Ethernet Frame.
312 Ethernet ethReply = new Ethernet();
313 ethReply.setSourceMACAddress(myMAC);
314 ethReply.setDestinationMACAddress(packet.getSourceMAC());
315 ethReply.setEtherType(Ethernet.TYPE_IPV4);
316 ethReply.setVlanID(packet.getVlanID());
317
318 // IP Packet
319 IPv4 ipv4Packet = (IPv4) packet.getPayload();
320 IPv4 ipv4Reply = new IPv4();
danielcd9deed2015-10-30 17:16:16 +0900321 ipv4Reply.setSourceAddress(dhcpServerReply.toInt());
samanwita pal2a313402015-09-14 16:03:22 -0700322 ipv4Reply.setDestinationAddress(ipOffered.toInt());
samanwita palf28207b2015-09-04 10:41:56 -0700323 ipv4Reply.setTtl(packetTTL);
324
325 // UDP Datagram.
326 UDP udpPacket = (UDP) ipv4Packet.getPayload();
327 UDP udpReply = new UDP();
328 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
329 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
330
331 // DHCP Payload.
332 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
333 DHCP dhcpReply = new DHCP();
334 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
samanwita pala3d1d1c2015-09-25 11:50:15 -0700335 dhcpReply.setFlags(dhcpPacket.getFlags());
336 dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
samanwita palf28207b2015-09-04 10:41:56 -0700337 dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700338 dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
339
Yi Tsengc7403c22017-06-19 16:23:22 -0700340 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700341 dhcpReply.setYourIPAddress(ipOffered.toInt());
danielcd9deed2015-10-30 17:16:16 +0900342 dhcpReply.setServerIPAddress(dhcpServerReply.toInt());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700343 if (dhcpPacket.getGatewayIPAddress() == 0) {
344 ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
345 }
346 }
samanwita palf28207b2015-09-04 10:41:56 -0700347 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
348 dhcpReply.setHardwareAddressLength((byte) 6);
349
350 // DHCP Options.
Yi Tsengc7403c22017-06-19 16:23:22 -0700351 DhcpOption option = new DhcpOption();
352 List<DhcpOption> optionList = new ArrayList<>();
samanwita palf28207b2015-09-04 10:41:56 -0700353
354 // DHCP Message Type.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700355 option.setCode(OptionCode_MessageType.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700356 option.setLength((byte) 1);
357 byte[] optionData = {outgoingMessageType};
358 option.setData(optionData);
359 optionList.add(option);
360
361 // DHCP Server Identifier.
Yi Tsengc7403c22017-06-19 16:23:22 -0700362 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700363 option.setCode(OptionCode_DHCPServerIp.getValue());
samanwita palf28207b2015-09-04 10:41:56 -0700364 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900365 option.setData(dhcpServerReply.toOctets());
samanwita palf28207b2015-09-04 10:41:56 -0700366 optionList.add(option);
367
Yi Tsengc7403c22017-06-19 16:23:22 -0700368 if (outgoingMessageType != DHCP.MsgType.DHCPNAK.getValue()) {
samanwita pala3d1d1c2015-09-25 11:50:15 -0700369 // IP Address Lease Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700370 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700371 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
372 option.setLength((byte) 4);
daniel02f3af02015-12-17 18:44:52 +0900373 option.setData(ByteBuffer.allocate(4)
374 .putInt(ipAssignment == null ? leaseTime : ipAssignment.leasePeriod()).array());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700375 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700376
samanwita pala3d1d1c2015-09-25 11:50:15 -0700377 // IP Address Renewal Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700378 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700379 option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
380 option.setLength((byte) 4);
381 option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
382 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700383
samanwita pala3d1d1c2015-09-25 11:50:15 -0700384 // IP Address Rebinding Time.
Yi Tsengc7403c22017-06-19 16:23:22 -0700385 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700386 option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
387 option.setLength((byte) 4);
388 option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
389 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700390
samanwita pala3d1d1c2015-09-25 11:50:15 -0700391 // Subnet Mask.
Yi Tsengc7403c22017-06-19 16:23:22 -0700392 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700393 option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
394 option.setLength((byte) 4);
danielcd9deed2015-10-30 17:16:16 +0900395 option.setData(subnetMaskReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700396 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700397
samanwita pala3d1d1c2015-09-25 11:50:15 -0700398 // Broadcast Address.
Yi Tsengc7403c22017-06-19 16:23:22 -0700399 option = new DhcpOption();
samanwita pala3d1d1c2015-09-25 11:50:15 -0700400 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
401 option.setLength((byte) 4);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700402 option.setData(broadcastReply.toOctets());
samanwita pala3d1d1c2015-09-25 11:50:15 -0700403 optionList.add(option);
samanwita palf28207b2015-09-04 10:41:56 -0700404
samanwita pala3d1d1c2015-09-25 11:50:15 -0700405 // Router Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700406 if (routerAddressReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700407 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700408 option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
409 option.setLength((byte) 4);
410 option.setData(routerAddressReply.get().toOctets());
411 optionList.add(option);
412 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700413
414 // DNS Server Address.
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700415 if (domainServerReply.isPresent()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700416 option = new DhcpOption();
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700417 option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
418 option.setLength((byte) 4);
419 option.setData(domainServerReply.get().toOctets());
420 optionList.add(option);
421 }
samanwita pala3d1d1c2015-09-25 11:50:15 -0700422 }
samanwita palf28207b2015-09-04 10:41:56 -0700423
424 // End Option.
Yi Tsengc7403c22017-06-19 16:23:22 -0700425 option = new DhcpOption();
samanwita palf28207b2015-09-04 10:41:56 -0700426 option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
427 option.setLength((byte) 1);
428 optionList.add(option);
429
430 dhcpReply.setOptions(optionList);
samanwita palf28207b2015-09-04 10:41:56 -0700431 udpReply.setPayload(dhcpReply);
432 ipv4Reply.setPayload(udpReply);
433 ethReply.setPayload(ipv4Reply);
434
435 return ethReply;
436 }
437
438 /**
439 * Sends the Ethernet reply frame via the Packet Service.
440 *
441 * @param context the context of the incoming frame
442 * @param reply the Ethernet reply frame
443 */
444 private void sendReply(PacketContext context, Ethernet reply) {
445 if (reply != null) {
446 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
447 ConnectPoint sourcePoint = context.inPacket().receivedFrom();
448 builder.setOutput(sourcePoint.port());
Thomas Vachuska00090442015-09-11 18:08:04 -0700449 context.block();
samanwita palf28207b2015-09-04 10:41:56 -0700450 packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
samanwita pal8969cbe2015-09-04 13:31:30 -0700451 builder.build(), ByteBuffer.wrap(reply.serialize())));
samanwita palf28207b2015-09-04 10:41:56 -0700452 }
453 }
454
455 /**
456 * Processes the DHCP Payload and initiates a reply to the client.
457 *
458 * @param context context of the incoming message
459 * @param dhcpPayload the extracted DHCP payload
460 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800461 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700462 if (dhcpPayload == null) {
463 log.debug("DHCP packet without payload, do nothing");
464 return;
465 }
466
samanwita palf28207b2015-09-04 10:41:56 -0700467 Ethernet packet = context.inPacket().parsed();
Yi Tsengc7403c22017-06-19 16:23:22 -0700468 DHCP.MsgType incomingPacketType = null;
samanwita palf28207b2015-09-04 10:41:56 -0700469 boolean flagIfRequestedIP = false;
470 boolean flagIfServerIP = false;
471 Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
472 Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
473
Yi Tsengc7403c22017-06-19 16:23:22 -0700474 for (DhcpOption option : dhcpPayload.getOptions()) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700475 if (option.getCode() == OptionCode_MessageType.getValue()) {
476 byte[] data = option.getData();
Yi Tsengc7403c22017-06-19 16:23:22 -0700477 incomingPacketType = DHCP.MsgType.getType(data[0]);
samanwita palf28207b2015-09-04 10:41:56 -0700478 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700479 if (option.getCode() == OptionCode_RequestedIP.getValue()) {
480 byte[] data = option.getData();
481 requestedIP = Ip4Address.valueOf(data);
482 flagIfRequestedIP = true;
483 }
484 if (option.getCode() == OptionCode_DHCPServerIp.getValue()) {
485 byte[] data = option.getData();
486 serverIP = Ip4Address.valueOf(data);
487 flagIfServerIP = true;
488 }
489 }
samanwita palf28207b2015-09-04 10:41:56 -0700490
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700491 if (incomingPacketType == null) {
492 log.debug("No incoming packet type specified, ignore it");
493 return;
494 }
samanwita palf28207b2015-09-04 10:41:56 -0700495
Yi Tsengc7403c22017-06-19 16:23:22 -0700496 DHCP.MsgType outgoingPacketType;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700497 MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
498 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
499 HostId hostId = HostId.hostId(clientMac, vlanId);
danielcd9deed2015-10-30 17:16:16 +0900500
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700501 switch (incomingPacketType) {
502 case DHCPDISCOVER:
503 log.trace("DHCP DISCOVER received from {}", hostId);
504 Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
samanwita pal2a313402015-09-14 16:03:22 -0700505 if (ipOffered != null) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700506 Ethernet ethReply = buildReply(
507 packet,
508 ipOffered,
Yi Tsengc7403c22017-06-19 16:23:22 -0700509 (byte) DHCP.MsgType.DHCPOFFER.getValue());
samanwita pal2a313402015-09-14 16:03:22 -0700510 sendReply(context, ethReply);
511 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700512 break;
513 case DHCPREQUEST:
514 log.trace("DHCP REQUEST received from {}", hostId);
515 if (flagIfServerIP && !myIP.equals(serverIP)) {
516 return;
517 }
samanwita palf28207b2015-09-04 10:41:56 -0700518
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700519 if (!flagIfRequestedIP) {
520 // this is renew or rebinding request
521 int clientIp = dhcpPayload.getClientIPAddress();
522 requestedIP = Ip4Address.valueOf(clientIp);
523 }
samanwita palf28207b2015-09-04 10:41:56 -0700524
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700525 IpAssignment ipAssignment = IpAssignment.builder()
526 .ipAddress(requestedIP)
527 .leasePeriod(leaseTime)
528 .timestamp(new Date())
529 .assignmentStatus(Option_Requested).build();
daniel877bb2f2015-11-12 21:33:05 +0900530
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700531 if (dhcpStore.assignIP(hostId, ipAssignment)) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700532 outgoingPacketType = DHCP.MsgType.DHCPACK;
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700533 discoverHost(context, requestedIP);
samanwita palf28207b2015-09-04 10:41:56 -0700534 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700535 outgoingPacketType = DHCP.MsgType.DHCPNAK;
samanwita palf28207b2015-09-04 10:41:56 -0700536 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700537
538 Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
539 sendReply(context, ethReply);
540 break;
541 case DHCPRELEASE:
542 log.trace("DHCP RELEASE received from {}", hostId);
543 Ip4Address releaseIp = dhcpStore.releaseIP(hostId);
544 if (releaseIp != null) {
545 hostProviderService.removeIpFromHost(hostId, releaseIp);
samanwita palc40e5ed2015-09-24 11:01:51 -0700546 }
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700547 break;
548 default:
549 break;
samanwita palf28207b2015-09-04 10:41:56 -0700550 }
551 }
552
553 /**
554 * Processes the ARP Payload and initiates a reply to the client.
555 *
556 * @param context context of the incoming message
557 * @param packet the ethernet payload
558 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800559 private void processArpPacket(PacketContext context, Ethernet packet) {
samanwita palf28207b2015-09-04 10:41:56 -0700560
561 ARP arpPacket = (ARP) packet.getPayload();
562
Ray Milkeyf0c47612017-09-28 11:29:38 -0700563 ARP arpReply = arpPacket.duplicate();
samanwita palf28207b2015-09-04 10:41:56 -0700564 arpReply.setOpCode(ARP.OP_REPLY);
565
566 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
567 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
568 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
569 arpReply.setSenderHardwareAddress(myMAC.toBytes());
570
571 // Ethernet Frame.
572 Ethernet ethReply = new Ethernet();
573 ethReply.setSourceMACAddress(myMAC);
574 ethReply.setDestinationMACAddress(packet.getSourceMAC());
575 ethReply.setEtherType(Ethernet.TYPE_ARP);
576 ethReply.setVlanID(packet.getVlanID());
577
578 ethReply.setPayload(arpReply);
579 sendReply(context, ethReply);
580 }
581
582 /**
583 * Integrates hosts learned through DHCP into topology.
584 * @param context context of the incoming message
585 * @param ipAssigned IP Address assigned to the host by DHCP Manager
586 */
587 private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700588 if (!allowHostDiscovery) {
589 // host discovery is not allowed, do nothing
590 return;
591 }
592
samanwita palf28207b2015-09-04 10:41:56 -0700593 Ethernet packet = context.inPacket().parsed();
594 MacAddress mac = packet.getSourceMAC();
595 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
596 HostLocation hostLocation = new HostLocation(context.inPacket().receivedFrom(), 0);
597
598 Set<IpAddress> ips = new HashSet<>();
599 ips.add(ipAssigned);
600
601 HostId hostId = HostId.hostId(mac, vlanId);
602 DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
Hyunsun Moon04b1fe92016-05-18 21:28:06 -0700603
604 log.info("Discovered host {}", desc);
Ray Milkeydc083442016-02-22 11:27:57 -0800605 hostProviderService.hostDetected(hostId, desc, false);
samanwita palf28207b2015-09-04 10:41:56 -0700606 }
607
608
609 @Override
610 public void process(PacketContext context) {
samanwita palf28207b2015-09-04 10:41:56 -0700611 Ethernet packet = context.inPacket().parsed();
612 if (packet == null) {
613 return;
614 }
615
616 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
617 IPv4 ipv4Packet = (IPv4) packet.getPayload();
618
619 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
620 UDP udpPacket = (UDP) ipv4Packet.getPayload();
621
622 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
623 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
624 // This is meant for the dhcp server so process the packet here.
625
626 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Jonathan Hartb35540a2015-11-17 09:30:56 -0800627 processDhcpPacket(context, dhcpPayload);
samanwita palf28207b2015-09-04 10:41:56 -0700628 }
629 }
630 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
631 ARP arpPacket = (ARP) packet.getPayload();
632
633 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700634 Objects.equals(myIP, Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
samanwita palf28207b2015-09-04 10:41:56 -0700635
Jonathan Hartb35540a2015-11-17 09:30:56 -0800636 processArpPacket(context, packet);
samanwita palf28207b2015-09-04 10:41:56 -0700637
638 }
639 }
640 }
641 }
642
643 private class InternalConfigListener implements NetworkConfigListener {
644
645 /**
646 * Reconfigures the DHCP Server according to the configuration parameters passed.
647 *
648 * @param cfg configuration object
649 */
Thomas Vachuskaa1da42e2015-09-09 00:45:22 -0700650 private void reconfigureNetwork(DhcpConfig cfg) {
Thomas Vachuska00090442015-09-11 18:08:04 -0700651 if (cfg == null) {
652 return;
653 }
samanwita palf28207b2015-09-04 10:41:56 -0700654 if (cfg.ip() != null) {
655 myIP = cfg.ip();
656 }
657 if (cfg.mac() != null) {
samanwita pal2a313402015-09-14 16:03:22 -0700658 myMAC = cfg.mac();
samanwita palf28207b2015-09-04 10:41:56 -0700659 }
660 if (cfg.subnetMask() != null) {
661 subnetMask = cfg.subnetMask();
662 }
663 if (cfg.broadcastAddress() != null) {
664 broadcastAddress = cfg.broadcastAddress();
665 }
666 if (cfg.routerAddress() != null) {
667 routerAddress = cfg.routerAddress();
668 }
669 if (cfg.domainServer() != null) {
670 domainServer = cfg.domainServer();
671 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700672 if (cfg.ttl() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700673 packetTTL = (byte) cfg.ttl();
samanwita palf28207b2015-09-04 10:41:56 -0700674 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700675 if (cfg.leaseTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700676 leaseTime = cfg.leaseTime();
samanwita palf28207b2015-09-04 10:41:56 -0700677 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700678 if (cfg.renewTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700679 renewalTime = cfg.renewTime();
samanwita palf28207b2015-09-04 10:41:56 -0700680 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700681 if (cfg.rebindTime() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700682 rebindingTime = cfg.rebindTime();
683 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700684 if (cfg.defaultTimeout() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700685 dhcpStore.setDefaultTimeoutForPurge(cfg.defaultTimeout());
686 }
samanwita pal7ccc2bc2015-09-14 19:53:15 -0700687 if (cfg.timerDelay() != -1) {
samanwita pal2a313402015-09-14 16:03:22 -0700688 timerDelay = cfg.timerDelay();
689 }
690 if ((cfg.startIp() != null) && (cfg.endIp() != null)) {
691 dhcpStore.populateIPPoolfromRange(cfg.startIp(), cfg.endIp());
samanwita palf28207b2015-09-04 10:41:56 -0700692 }
693 }
694
samanwita palf28207b2015-09-04 10:41:56 -0700695
696 @Override
697 public void event(NetworkConfigEvent event) {
698
699 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
samanwita pal2a313402015-09-14 16:03:22 -0700700 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
701 event.configClass().equals(DhcpConfig.class)) {
702
703 DhcpConfig cfg = cfgService.getConfig(appId, DhcpConfig.class);
704 reconfigureNetwork(cfg);
705 log.info("Reconfigured");
samanwita palf28207b2015-09-04 10:41:56 -0700706 }
707 }
708 }
709
710 private class InternalHostProvider extends AbstractProvider implements HostProvider {
711
712 /**
713 * Creates a provider with the supplier identifier.
714 */
715 protected InternalHostProvider() {
716 super(PID);
717 }
718
719 @Override
720 public void triggerProbe(Host host) {
721 // nothing to do
722 }
723 }
samanwita pal2a313402015-09-14 16:03:22 -0700724
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700725 private class PurgeListTask implements Runnable {
samanwita pal2a313402015-09-14 16:03:22 -0700726
727 @Override
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700728 public void run() {
samanwita pal2a313402015-09-14 16:03:22 -0700729 IpAssignment ipAssignment;
730 Date dateNow = new Date();
731
samanwita pal0bff4302015-09-15 13:37:00 -0700732 Map<HostId, IpAssignment> ipAssignmentMap = dhcpStore.listAllMapping();
samanwita pal2a313402015-09-14 16:03:22 -0700733 for (Map.Entry<HostId, IpAssignment> entry: ipAssignmentMap.entrySet()) {
734 ipAssignment = entry.getValue();
735
736 long timeLapsed = dateNow.getTime() - ipAssignment.timestamp().getTime();
737 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
738 (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
739
samanwita palc40e5ed2015-09-24 11:01:51 -0700740 Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
741 if (ip4Address != null) {
742 hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
743 }
samanwita pal2a313402015-09-14 16:03:22 -0700744 }
745 }
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700746 timeout = SharedScheduledExecutors.newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
samanwita pal2a313402015-09-14 16:03:22 -0700747 }
748 }
Jonathan Hartb35540a2015-11-17 09:30:56 -0800749}