blob: 9785c3c70ee21d36246e5284bc5284720925b6a3 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +09003 *
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.openstacknetworking.impl;
17
18import com.google.common.base.Strings;
Daniel Parkd9d4c292018-06-26 20:33:58 +090019import com.google.common.collect.ImmutableList;
Hyunsun Moon44aac662017-02-18 02:07:01 +090020import com.google.common.collect.Lists;
Hyunsun Moon44aac662017-02-18 02:07:01 +090021import org.onlab.packet.DHCP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090022import org.onlab.packet.Ethernet;
23import org.onlab.packet.IPv4;
24import org.onlab.packet.Ip4Address;
Daniel Park4d421002018-07-27 23:36:57 +090025import org.onlab.packet.IpAddress;
Hyunsun Moon44aac662017-02-18 02:07:01 +090026import org.onlab.packet.IpPrefix;
27import org.onlab.packet.MacAddress;
28import org.onlab.packet.TpPort;
29import org.onlab.packet.UDP;
daniel park796c2eb2018-03-22 17:01:51 +090030import org.onlab.packet.dhcp.DhcpOption;
Hyunsun Moon44aac662017-02-18 02:07:01 +090031import org.onlab.util.Tools;
32import org.onosproject.cfg.ComponentConfigService;
daniel park15506e82018-04-04 18:52:16 +090033import org.onosproject.cluster.ClusterService;
34import org.onosproject.cluster.LeadershipService;
35import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.packet.DefaultOutboundPacket;
44import org.onosproject.net.packet.PacketContext;
Hyunsun Moon44aac662017-02-18 02:07:01 +090045import org.onosproject.net.packet.PacketProcessor;
46import org.onosproject.net.packet.PacketService;
47import org.onosproject.openstacknetworking.api.Constants;
48import org.onosproject.openstacknetworking.api.InstancePort;
49import org.onosproject.openstacknetworking.api.InstancePortService;
daniel park796c2eb2018-03-22 17:01:51 +090050import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090051import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
daniel park15506e82018-04-04 18:52:16 +090052import org.onosproject.openstacknode.api.OpenstackNode;
53import org.onosproject.openstacknode.api.OpenstackNodeEvent;
54import org.onosproject.openstacknode.api.OpenstackNodeListener;
daniel park796c2eb2018-03-22 17:01:51 +090055import org.onosproject.openstacknode.api.OpenstackNodeService;
Daniel Parkd9d4c292018-06-26 20:33:58 +090056import org.openstack4j.model.network.HostRoute;
Hyunsun Moon44aac662017-02-18 02:07:01 +090057import org.openstack4j.model.network.IP;
Daniel Park468e7852018-07-28 00:38:45 +090058import org.openstack4j.model.network.Network;
Hyunsun Moon44aac662017-02-18 02:07:01 +090059import org.openstack4j.model.network.Port;
60import org.openstack4j.model.network.Subnet;
61import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062import org.osgi.service.component.annotations.Activate;
63import org.osgi.service.component.annotations.Component;
64import org.osgi.service.component.annotations.Deactivate;
65import org.osgi.service.component.annotations.Modified;
66import org.osgi.service.component.annotations.Reference;
67import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon44aac662017-02-18 02:07:01 +090068import org.slf4j.Logger;
69
70import java.nio.ByteBuffer;
71import java.util.Dictionary;
72import java.util.List;
daniel park15506e82018-04-04 18:52:16 +090073import java.util.Objects;
Jian Li32b03622018-11-06 17:54:24 +090074import java.util.concurrent.ExecutorService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090075
Daniel Parkf8e422d2018-07-30 14:14:37 +090076import static com.google.common.base.Preconditions.checkNotNull;
Jian Li32b03622018-11-06 17:54:24 +090077import static java.util.concurrent.Executors.newSingleThreadExecutor;
daniel park796c2eb2018-03-22 17:01:51 +090078import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_BroadcastAddress;
Daniel Parkd9d4c292018-06-26 20:33:58 +090079import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_Classless_Static_Route;
daniel park796c2eb2018-03-22 17:01:51 +090080import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
81import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DomainServer;
82import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
83import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_LeaseTime;
84import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
Daniel Park48f10332018-08-02 10:57:27 +090085import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RouterAddress;
daniel park796c2eb2018-03-22 17:01:51 +090086import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_SubnetMask;
Yi Tsengc7403c22017-06-19 16:23:22 -070087import static org.onlab.packet.DHCP.MsgType.DHCPACK;
88import static org.onlab.packet.DHCP.MsgType.DHCPOFFER;
Jian Li32b03622018-11-06 17:54:24 +090089import static org.onlab.util.Tools.groupedThreads;
Jian Li5c09e212018-10-24 18:23:58 +090090import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
daniel park796c2eb2018-03-22 17:01:51 +090091import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
Ray Milkey8e406512018-10-24 15:56:50 -070092import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC;
93import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC_DEFAULT;
Jian Lifb64d882018-11-27 10:57:40 +090094import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090095import static org.slf4j.LoggerFactory.getLogger;
96
97/**
98 * Handles DHCP requests for the virtual instances.
99 */
Ray Milkey8e406512018-10-24 15:56:50 -0700100@Component(
101 immediate = true,
102 property = {
103 DHCP_SERVER_MAC + "=" + DHCP_SERVER_MAC_DEFAULT
104 }
105)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900106public class OpenstackSwitchingDhcpHandler {
107 protected final Logger log = getLogger(getClass());
108
Daniel Park4d421002018-07-27 23:36:57 +0900109 private static final Ip4Address DEFAULT_PRIMARY_DNS = Ip4Address.valueOf("8.8.8.8");
110 private static final Ip4Address DEFAULT_SECONDARY_DNS = Ip4Address.valueOf("8.8.4.4");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111 private static final byte PACKET_TTL = (byte) 127;
112 // TODO add MTU, static route option codes to ONOS DHCP and remove here
113 private static final byte DHCP_OPTION_MTU = (byte) 26;
114 private static final byte[] DHCP_DATA_LEASE_INFINITE =
115 ByteBuffer.allocate(4).putInt(-1).array();
Jian Lifb005492018-03-02 10:50:15 +0900116 // we are using 1450 as a default DHCP MTU value
117 private static final int DHCP_DATA_MTU_DEFAULT = 1450;
Daniel Parkd9d4c292018-06-26 20:33:58 +0900118 private static final int OCTET_BIT_LENGTH = 8;
119 private static final int V4_BYTE_SIZE = 4;
Daniel Parkf8e422d2018-07-30 14:14:37 +0900120 private static final int V4_CIDR_LOWER_BOUND = -1;
Daniel Parkd9d4c292018-06-26 20:33:58 +0900121 private static final int V4_CIDR_UPPER_BOUND = 33;
122 private static final int PADDING_SIZE = 4;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125 protected CoreService coreService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900128 protected ComponentConfigService configService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900131 protected PacketService packetService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900134 protected InstancePortService instancePortService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900137 protected OpenstackNetworkService osNetworkService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park796c2eb2018-03-22 17:01:51 +0900140 protected OpenstackNodeService osNodeService;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park796c2eb2018-03-22 17:01:51 +0900143 protected OpenstackFlowRuleService osFlowRuleService;
144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park15506e82018-04-04 18:52:16 +0900146 protected ClusterService clusterService;
147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park15506e82018-04-04 18:52:16 +0900149 protected LeadershipService leadershipService;
150
Ray Milkey8e406512018-10-24 15:56:50 -0700151 /** Fake MAC address for virtual network subnet gateway. */
152 private String dhcpServerMac = DHCP_SERVER_MAC_DEFAULT;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900153
Jian Lifb005492018-03-02 10:50:15 +0900154 private int dhcpDataMtu = DHCP_DATA_MTU_DEFAULT;
155
Hyunsun Moon44aac662017-02-18 02:07:01 +0900156 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
daniel park15506e82018-04-04 18:52:16 +0900157 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900158
Jian Li32b03622018-11-06 17:54:24 +0900159 private final ExecutorService eventExecutor = newSingleThreadExecutor(
160 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
161
Hyunsun Moon44aac662017-02-18 02:07:01 +0900162 private ApplicationId appId;
daniel park15506e82018-04-04 18:52:16 +0900163 private NodeId localNodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900164
165 @Activate
166 protected void activate() {
167 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
daniel park15506e82018-04-04 18:52:16 +0900168 localNodeId = clusterService.getLocalNode().id();
169 osNodeService.addListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900170 configService.registerProperties(getClass());
171 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
daniel park15506e82018-04-04 18:52:16 +0900172 leadershipService.runForLeadership(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900173
174 log.info("Started");
175 }
176
177 @Deactivate
178 protected void deactivate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900179 packetService.removeProcessor(packetProcessor);
daniel park15506e82018-04-04 18:52:16 +0900180 osNodeService.removeListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900181 configService.unregisterProperties(getClass(), false);
daniel park15506e82018-04-04 18:52:16 +0900182 leadershipService.withdraw(appId.name());
Jian Li32b03622018-11-06 17:54:24 +0900183 eventExecutor.shutdown();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184
185 log.info("Stopped");
186 }
187
188 @Modified
189 protected void modified(ComponentContext context) {
190 Dictionary<?, ?> properties = context.getProperties();
191 String updatedMac;
192
193 updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
Jian Lifb005492018-03-02 10:50:15 +0900194
Hyunsun Moon44aac662017-02-18 02:07:01 +0900195 if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
196 dhcpServerMac = updatedMac;
197 }
198
199 log.info("Modified");
200 }
201
Hyunsun Moon44aac662017-02-18 02:07:01 +0900202 private class InternalPacketProcessor implements PacketProcessor {
203
204 @Override
205 public void process(PacketContext context) {
206 if (context.isHandled()) {
207 return;
208 }
209
210 Ethernet ethPacket = context.inPacket().parsed();
211 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) {
212 return;
213 }
214 IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
215 if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_UDP) {
216 return;
217 }
218 UDP udpPacket = (UDP) ipv4Packet.getPayload();
219 if (udpPacket.getDestinationPort() != UDP.DHCP_SERVER_PORT ||
220 udpPacket.getSourcePort() != UDP.DHCP_CLIENT_PORT) {
221 return;
222 }
223
224 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
Jian Li32b03622018-11-06 17:54:24 +0900225
226 eventExecutor.execute(() -> processDhcp(context, dhcpPacket));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900227 }
228
229 private void processDhcp(PacketContext context, DHCP dhcpPacket) {
230 if (dhcpPacket == null) {
231 log.trace("DHCP packet without payload received, do nothing");
232 return;
233 }
234
Yi Tsengc7403c22017-06-19 16:23:22 -0700235 DHCP.MsgType inPacketType = getPacketType(dhcpPacket);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900236 if (inPacketType == null || dhcpPacket.getClientHardwareAddress() == null) {
237 log.trace("Malformed DHCP packet received, ignore it");
238 return;
239 }
240
241 MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
242 InstancePort reqInstPort = instancePortService.instancePort(clientMac);
243 if (reqInstPort == null) {
244 log.trace("Failed to find host(MAC:{})", clientMac);
245 return;
246 }
247 Ethernet ethPacket = context.inPacket().parsed();
248 switch (inPacketType) {
249 case DHCPDISCOVER:
250 log.trace("DHCP DISCOVER received from {}", clientMac);
251 Ethernet discoverReply = buildReply(
252 ethPacket,
253 (byte) DHCPOFFER.getValue(),
254 reqInstPort);
255 sendReply(context, discoverReply);
256 log.trace("DHCP OFFER({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900257 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900258 break;
259 case DHCPREQUEST:
260 log.trace("DHCP REQUEST received from {}", clientMac);
261 Ethernet requestReply = buildReply(
262 ethPacket,
263 (byte) DHCPACK.getValue(),
264 reqInstPort);
265 sendReply(context, requestReply);
266 log.trace("DHCP ACK({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900267 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900268 break;
269 case DHCPRELEASE:
270 log.trace("DHCP RELEASE received from {}", clientMac);
271 // do nothing
272 break;
273 default:
274 break;
275 }
276 }
277
Yi Tsengc7403c22017-06-19 16:23:22 -0700278 private DHCP.MsgType getPacketType(DHCP dhcpPacket) {
279 DhcpOption optType = dhcpPacket.getOption(OptionCode_MessageType);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900280 if (optType == null) {
281 log.trace("DHCP packet with no message type, ignore it");
282 return null;
283 }
284
Yi Tsengc7403c22017-06-19 16:23:22 -0700285 DHCP.MsgType inPacketType = DHCP.MsgType.getType(optType.getData()[0]);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900286 if (inPacketType == null) {
287 log.trace("DHCP packet with no packet type, ignore it");
288 }
289 return inPacketType;
290 }
291
292 private Ethernet buildReply(Ethernet ethRequest, byte packetType,
293 InstancePort reqInstPort) {
Daniel Parke0945c12018-08-28 17:22:25 +0900294 log.trace("Build for DHCP reply msg for instance port {}", reqInstPort.toString());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900295 Port osPort = osNetworkService.port(reqInstPort.portId());
Daniel Parke0945c12018-08-28 17:22:25 +0900296 if (osPort == null) {
297 log.error("Failed to retrieve openstack port information for instance port {}",
298 reqInstPort.toString());
Ray Milkey92815932018-08-28 10:32:17 -0700299 return null;
Daniel Parke0945c12018-08-28 17:22:25 +0900300 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900301 // pick one IP address to make a reply
302 IP fixedIp = osPort.getFixedIps().stream().findFirst().get();
303 Subnet osSubnet = osNetworkService.subnet(fixedIp.getSubnetId());
304
305 Ethernet ethReply = new Ethernet();
306 ethReply.setSourceMACAddress(dhcpServerMac);
307 ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
308 ethReply.setEtherType(Ethernet.TYPE_IPV4);
309
310 IPv4 ipv4Request = (IPv4) ethRequest.getPayload();
311 IPv4 ipv4Reply = new IPv4();
daniel park15506e82018-04-04 18:52:16 +0900312
313 ipv4Reply.setSourceAddress(clusterService.getLocalNode().ip().getIp4Address().toString());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900314 ipv4Reply.setDestinationAddress(reqInstPort.ipAddress().getIp4Address().toInt());
315 ipv4Reply.setTtl(PACKET_TTL);
316
317 UDP udpRequest = (UDP) ipv4Request.getPayload();
318 UDP udpReply = new UDP();
319 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
320 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
321
322 DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
323 DHCP dhcpReply = buildDhcpReply(
324 dhcpRequest,
325 packetType,
326 reqInstPort.ipAddress().getIp4Address(),
327 osSubnet);
328
329 udpReply.setPayload(dhcpReply);
330 ipv4Reply.setPayload(udpReply);
331 ethReply.setPayload(ipv4Reply);
332
333 return ethReply;
334 }
335
336 private void sendReply(PacketContext context, Ethernet ethReply) {
337 if (ethReply == null) {
338 return;
339 }
340 ConnectPoint srcPoint = context.inPacket().receivedFrom();
341 TrafficTreatment treatment = DefaultTrafficTreatment
342 .builder()
343 .setOutput(srcPoint.port())
344 .build();
345
346 packetService.emit(new DefaultOutboundPacket(
347 srcPoint.deviceId(),
348 treatment,
349 ByteBuffer.wrap(ethReply.serialize())));
350 context.block();
351 }
352
353 private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
354 Subnet osSubnet) {
daniel park15506e82018-04-04 18:52:16 +0900355 Ip4Address gatewayIp = clusterService.getLocalNode().ip().getIp4Address();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900356 int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
357
358 DHCP dhcpReply = new DHCP();
359 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
360 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
361 dhcpReply.setHardwareAddressLength((byte) 6);
362 dhcpReply.setTransactionId(request.getTransactionId());
363 dhcpReply.setFlags(request.getFlags());
364 dhcpReply.setYourIPAddress(yourIp.toInt());
365 dhcpReply.setServerIPAddress(gatewayIp.toInt());
366 dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress());
367
Yi Tsengc7403c22017-06-19 16:23:22 -0700368 List<DhcpOption> options = Lists.newArrayList();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900369 // message type
Yi Tsengc7403c22017-06-19 16:23:22 -0700370 DhcpOption option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900371 option.setCode(OptionCode_MessageType.getValue());
372 option.setLength((byte) 1);
373 byte[] optionData = {msgType};
374 option.setData(optionData);
375 options.add(option);
376
377 // server identifier
Yi Tsengc7403c22017-06-19 16:23:22 -0700378 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900379 option.setCode(OptionCode_DHCPServerIp.getValue());
380 option.setLength((byte) 4);
381 option.setData(gatewayIp.toOctets());
382 options.add(option);
383
384 // lease time
Yi Tsengc7403c22017-06-19 16:23:22 -0700385 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900386 option.setCode(OptionCode_LeaseTime.getValue());
387 option.setLength((byte) 4);
388 option.setData(DHCP_DATA_LEASE_INFINITE);
389 options.add(option);
390
391 // subnet mask
392 Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700393 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900394 option.setCode(OptionCode_SubnetMask.getValue());
395 option.setLength((byte) 4);
396 option.setData(subnetMask.toOctets());
397 options.add(option);
398
399 // broadcast address
400 Ip4Address broadcast = Ip4Address.makeMaskedAddress(yourIp, subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700401 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900402 option.setCode(OptionCode_BroadcastAddress.getValue());
403 option.setLength((byte) 4);
404 option.setData(broadcast.toOctets());
405 options.add(option);
406
407 // domain server
Yi Tsengc7403c22017-06-19 16:23:22 -0700408 option = new DhcpOption();
Daniel Park4d421002018-07-27 23:36:57 +0900409
410 List<String> dnsServers = osSubnet.getDnsNames();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900411 option.setCode(OptionCode_DomainServer.getValue());
Daniel Park4d421002018-07-27 23:36:57 +0900412
413 if (dnsServers.isEmpty()) {
414 option.setLength((byte) 8);
415 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
416 dnsByteBuf.put(DEFAULT_PRIMARY_DNS.toOctets());
417 dnsByteBuf.put(DEFAULT_SECONDARY_DNS.toOctets());
418
419 option.setData(dnsByteBuf.array());
420 } else {
421 int dnsLength = 4 * dnsServers.size();
422
423 option.setLength((byte) dnsLength);
424
425 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
426
427 for (int i = 0; i < dnsServers.size(); i++) {
428 dnsByteBuf.put(IpAddress.valueOf(dnsServers.get(i)).toOctets());
429 }
430 option.setData(dnsByteBuf.array());
431 }
432
Hyunsun Moon44aac662017-02-18 02:07:01 +0900433 options.add(option);
434
Yi Tsengc7403c22017-06-19 16:23:22 -0700435 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900436 option.setCode(DHCP_OPTION_MTU);
437 option.setLength((byte) 2);
Daniel Park468e7852018-07-28 00:38:45 +0900438 Network osNetwork = osNetworkService.network(osSubnet.getNetworkId());
439 checkNotNull(osNetwork);
440 checkNotNull(osNetwork.getMTU());
441
442 option.setData(ByteBuffer.allocate(2).putShort(osNetwork.getMTU().shortValue()).array());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900443 options.add(option);
444
Daniel Parkd9d4c292018-06-26 20:33:58 +0900445 // classless static route
446 if (!osSubnet.getHostRoutes().isEmpty()) {
447 option = new DhcpOption();
448 option.setCode(OptionCode_Classless_Static_Route.getValue());
449
450 int hostRoutesSize = hostRoutesSize(ImmutableList.copyOf(osSubnet.getHostRoutes()));
451 if (hostRoutesSize == 0) {
452 throw new IllegalArgumentException("Illegal CIDR hostRoutesSize value!");
453 }
454
455 log.trace("hostRouteSize: {}", hostRoutesSize);
456
457 option.setLength((byte) hostRoutesSize);
458 ByteBuffer hostRouteByteBuf = ByteBuffer.allocate(hostRoutesSize);
459
460 osSubnet.getHostRoutes().forEach(h -> {
461 log.debug("processing host route information: {}", h.toString());
462
463 IpPrefix ipPrefix = IpPrefix.valueOf(h.getDestination());
464
465 hostRouteByteBuf.put(bytesDestinationDescriptor(ipPrefix));
466
467 hostRouteByteBuf.put(Ip4Address.valueOf(h.getNexthop()).toOctets());
468 });
469
470 option.setData(hostRouteByteBuf.array());
471
472 options.add(option);
473 }
474
Daniel Park48f10332018-08-02 10:57:27 +0900475 // Sets the default router address up.
476 // Performs only if the gateway is set in subnet.
477 if (!Strings.isNullOrEmpty(osSubnet.getGateway())) {
478 option = new DhcpOption();
479 option.setCode(OptionCode_RouterAddress.getValue());
480 option.setLength((byte) 4);
481 option.setData(Ip4Address.valueOf(osSubnet.getGateway()).toOctets());
482 options.add(option);
483 }
484
Hyunsun Moon44aac662017-02-18 02:07:01 +0900485 // end option
Yi Tsengc7403c22017-06-19 16:23:22 -0700486 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900487 option.setCode(OptionCode_END.getValue());
488 option.setLength((byte) 1);
489 options.add(option);
490
491 dhcpReply.setOptions(options);
492 return dhcpReply;
493 }
Daniel Parkd9d4c292018-06-26 20:33:58 +0900494
495 private int hostRoutesSize(List<HostRoute> hostRoutes) {
496 int size = 0;
497 int preFixLen;
498
499 for (HostRoute h : hostRoutes) {
500 preFixLen = IpPrefix.valueOf(h.getDestination()).prefixLength();
501 if (Math.max(V4_CIDR_LOWER_BOUND, preFixLen) == V4_CIDR_LOWER_BOUND ||
502 Math.min(preFixLen, V4_CIDR_UPPER_BOUND) == V4_CIDR_UPPER_BOUND) {
503 throw new IllegalArgumentException("Illegal CIDR length value!");
504 }
505
Daniel Parkf8e422d2018-07-30 14:14:37 +0900506 for (int i = 0; i <= V4_BYTE_SIZE; i++) {
Daniel Parkd9d4c292018-06-26 20:33:58 +0900507 if (preFixLen == Math.min(preFixLen, i * OCTET_BIT_LENGTH)) {
508 size = size + i + 1 + PADDING_SIZE;
509 break;
510 }
511 }
512 }
513 return size;
514 }
515
516 private byte[] bytesDestinationDescriptor(IpPrefix ipPrefix) {
517 ByteBuffer byteBuffer;
518 int prefixLen = ipPrefix.prefixLength();
519
520 // retrieve ipPrefix to the destination descriptor format
521 // ex) 10.1.1.0/24 -> [10,1,1,0]
522 String[] ipPrefixString = ipPrefix.getIp4Prefix().toString()
523 .split("/")[0]
524 .split("\\.");
525
Daniel Parkf8e422d2018-07-30 14:14:37 +0900526 // retrieve destination descriptor and put this to bytebuffer according to RFC 3442
527 // ex) 0.0.0.0/0 -> 0
Daniel Parkd9d4c292018-06-26 20:33:58 +0900528 // ex) 10.0.0.0/8 -> 8.10
529 // ex) 10.17.0.0/16 -> 16.10.17
530 // ex) 10.27.129.0/24 -> 24.10.27.129
531 // ex) 10.229.0.128/25 -> 25.10.229.0.128
Daniel Parkf8e422d2018-07-30 14:14:37 +0900532 for (int i = 0; i <= V4_BYTE_SIZE; i++) {
Daniel Parkd9d4c292018-06-26 20:33:58 +0900533 if (prefixLen == Math.min(prefixLen, i * OCTET_BIT_LENGTH)) {
534 byteBuffer = ByteBuffer.allocate(i + 1);
535 byteBuffer.put((byte) prefixLen);
536
537 for (int j = 0; j < i; j++) {
538 byteBuffer.put((byte) Integer.parseInt(ipPrefixString[j]));
539 }
540 return byteBuffer.array();
541 }
542 }
543
544 return null;
545 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900546 }
daniel park15506e82018-04-04 18:52:16 +0900547
548 private class InternalNodeEventListener implements OpenstackNodeListener {
Jian Lifb64d882018-11-27 10:57:40 +0900549 @Override
550 public boolean isRelevant(OpenstackNodeEvent event) {
551 return event.subject().type() == COMPUTE;
552 }
553
Jian Li34220ea2018-11-14 01:30:24 +0900554 private boolean isRelevantHelper() {
555 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
daniel park15506e82018-04-04 18:52:16 +0900556 }
557
558 @Override
559 public void event(OpenstackNodeEvent event) {
560 OpenstackNode osNode = event.subject();
561 switch (event.type()) {
562 case OPENSTACK_NODE_COMPLETE:
Jian Li34220ea2018-11-14 01:30:24 +0900563 eventExecutor.execute(() -> {
564
565 if (!isRelevantHelper()) {
566 return;
567 }
568
569 setDhcpRule(osNode, true);
570 });
daniel park15506e82018-04-04 18:52:16 +0900571 break;
572 case OPENSTACK_NODE_INCOMPLETE:
Jian Li34220ea2018-11-14 01:30:24 +0900573 eventExecutor.execute(() -> {
574
575 if (!isRelevantHelper()) {
576 return;
577 }
578
579 setDhcpRule(osNode, false);
580 });
daniel park15506e82018-04-04 18:52:16 +0900581 break;
582 case OPENSTACK_NODE_CREATED:
583 case OPENSTACK_NODE_UPDATED:
584 case OPENSTACK_NODE_REMOVED:
585 default:
586 break;
587 }
588 }
589
590 private void setDhcpRule(OpenstackNode openstackNode, boolean install) {
daniel park15506e82018-04-04 18:52:16 +0900591 TrafficSelector selector = DefaultTrafficSelector.builder()
592 .matchEthType(Ethernet.TYPE_IPV4)
593 .matchIPProtocol(IPv4.PROTOCOL_UDP)
594 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
595 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
596 .build();
597
598 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
599 .punt()
600 .build();
601
602 osFlowRuleService.setRule(
603 appId,
604 openstackNode.intgBridge(),
605 selector,
606 treatment,
607 PRIORITY_DHCP_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900608 DHCP_TABLE,
daniel park15506e82018-04-04 18:52:16 +0900609 install);
610 }
611 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900612}