blob: 71fa4d16df920be5822e5c9771e4eea21d9991ef [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;
Hyunsun Moon44aac662017-02-18 02:07:01 +090074
Daniel Parkf8e422d2018-07-30 14:14:37 +090075import static com.google.common.base.Preconditions.checkNotNull;
daniel park796c2eb2018-03-22 17:01:51 +090076import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_BroadcastAddress;
Daniel Parkd9d4c292018-06-26 20:33:58 +090077import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_Classless_Static_Route;
daniel park796c2eb2018-03-22 17:01:51 +090078import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
79import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DomainServer;
80import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
81import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_LeaseTime;
82import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
Daniel Park48f10332018-08-02 10:57:27 +090083import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RouterAddress;
daniel park796c2eb2018-03-22 17:01:51 +090084import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_SubnetMask;
Yi Tsengc7403c22017-06-19 16:23:22 -070085import static org.onlab.packet.DHCP.MsgType.DHCPACK;
86import static org.onlab.packet.DHCP.MsgType.DHCPOFFER;
Jian Li5c09e212018-10-24 18:23:58 +090087import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
daniel park796c2eb2018-03-22 17:01:51 +090088import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
Ray Milkey8e406512018-10-24 15:56:50 -070089import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC;
90import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC_DEFAULT;
Jian Li51b844c2018-05-31 10:59:03 +090091import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090092import static org.slf4j.LoggerFactory.getLogger;
93
94/**
95 * Handles DHCP requests for the virtual instances.
96 */
Ray Milkey8e406512018-10-24 15:56:50 -070097@Component(
98 immediate = true,
99 property = {
100 DHCP_SERVER_MAC + "=" + DHCP_SERVER_MAC_DEFAULT
101 }
102)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103public class OpenstackSwitchingDhcpHandler {
104 protected final Logger log = getLogger(getClass());
105
Daniel Park4d421002018-07-27 23:36:57 +0900106 private static final Ip4Address DEFAULT_PRIMARY_DNS = Ip4Address.valueOf("8.8.8.8");
107 private static final Ip4Address DEFAULT_SECONDARY_DNS = Ip4Address.valueOf("8.8.4.4");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108 private static final byte PACKET_TTL = (byte) 127;
109 // TODO add MTU, static route option codes to ONOS DHCP and remove here
110 private static final byte DHCP_OPTION_MTU = (byte) 26;
111 private static final byte[] DHCP_DATA_LEASE_INFINITE =
112 ByteBuffer.allocate(4).putInt(-1).array();
Jian Lifb005492018-03-02 10:50:15 +0900113 // we are using 1450 as a default DHCP MTU value
114 private static final int DHCP_DATA_MTU_DEFAULT = 1450;
Daniel Parkd9d4c292018-06-26 20:33:58 +0900115 private static final int OCTET_BIT_LENGTH = 8;
116 private static final int V4_BYTE_SIZE = 4;
Daniel Parkf8e422d2018-07-30 14:14:37 +0900117 private static final int V4_CIDR_LOWER_BOUND = -1;
Daniel Parkd9d4c292018-06-26 20:33:58 +0900118 private static final int V4_CIDR_UPPER_BOUND = 33;
119 private static final int PADDING_SIZE = 4;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122 protected CoreService coreService;
123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125 protected ComponentConfigService configService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900128 protected PacketService packetService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900131 protected InstancePortService instancePortService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900134 protected OpenstackNetworkService osNetworkService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park796c2eb2018-03-22 17:01:51 +0900137 protected OpenstackNodeService osNodeService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park796c2eb2018-03-22 17:01:51 +0900140 protected OpenstackFlowRuleService osFlowRuleService;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park15506e82018-04-04 18:52:16 +0900143 protected ClusterService clusterService;
144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park15506e82018-04-04 18:52:16 +0900146 protected LeadershipService leadershipService;
147
Ray Milkey8e406512018-10-24 15:56:50 -0700148 /** Fake MAC address for virtual network subnet gateway. */
149 private String dhcpServerMac = DHCP_SERVER_MAC_DEFAULT;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900150
Jian Lifb005492018-03-02 10:50:15 +0900151 private int dhcpDataMtu = DHCP_DATA_MTU_DEFAULT;
152
Hyunsun Moon44aac662017-02-18 02:07:01 +0900153 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
daniel park15506e82018-04-04 18:52:16 +0900154 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900155
156 private ApplicationId appId;
daniel park15506e82018-04-04 18:52:16 +0900157 private NodeId localNodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900158
159 @Activate
160 protected void activate() {
161 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
daniel park15506e82018-04-04 18:52:16 +0900162 localNodeId = clusterService.getLocalNode().id();
163 osNodeService.addListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900164 configService.registerProperties(getClass());
165 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
daniel park15506e82018-04-04 18:52:16 +0900166 leadershipService.runForLeadership(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900167
168 log.info("Started");
169 }
170
171 @Deactivate
172 protected void deactivate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900173 packetService.removeProcessor(packetProcessor);
daniel park15506e82018-04-04 18:52:16 +0900174 osNodeService.removeListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900175 configService.unregisterProperties(getClass(), false);
daniel park15506e82018-04-04 18:52:16 +0900176 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900177
178 log.info("Stopped");
179 }
180
181 @Modified
182 protected void modified(ComponentContext context) {
183 Dictionary<?, ?> properties = context.getProperties();
184 String updatedMac;
185
186 updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
Jian Lifb005492018-03-02 10:50:15 +0900187
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188 if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
189 dhcpServerMac = updatedMac;
190 }
191
192 log.info("Modified");
193 }
194
Hyunsun Moon44aac662017-02-18 02:07:01 +0900195 private class InternalPacketProcessor implements PacketProcessor {
196
197 @Override
198 public void process(PacketContext context) {
199 if (context.isHandled()) {
200 return;
201 }
202
203 Ethernet ethPacket = context.inPacket().parsed();
204 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) {
205 return;
206 }
207 IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
208 if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_UDP) {
209 return;
210 }
211 UDP udpPacket = (UDP) ipv4Packet.getPayload();
212 if (udpPacket.getDestinationPort() != UDP.DHCP_SERVER_PORT ||
213 udpPacket.getSourcePort() != UDP.DHCP_CLIENT_PORT) {
214 return;
215 }
216
217 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
218 processDhcp(context, dhcpPacket);
219 }
220
221 private void processDhcp(PacketContext context, DHCP dhcpPacket) {
222 if (dhcpPacket == null) {
223 log.trace("DHCP packet without payload received, do nothing");
224 return;
225 }
226
Yi Tsengc7403c22017-06-19 16:23:22 -0700227 DHCP.MsgType inPacketType = getPacketType(dhcpPacket);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900228 if (inPacketType == null || dhcpPacket.getClientHardwareAddress() == null) {
229 log.trace("Malformed DHCP packet received, ignore it");
230 return;
231 }
232
233 MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
234 InstancePort reqInstPort = instancePortService.instancePort(clientMac);
235 if (reqInstPort == null) {
236 log.trace("Failed to find host(MAC:{})", clientMac);
237 return;
238 }
239 Ethernet ethPacket = context.inPacket().parsed();
240 switch (inPacketType) {
241 case DHCPDISCOVER:
242 log.trace("DHCP DISCOVER received from {}", clientMac);
243 Ethernet discoverReply = buildReply(
244 ethPacket,
245 (byte) DHCPOFFER.getValue(),
246 reqInstPort);
247 sendReply(context, discoverReply);
248 log.trace("DHCP OFFER({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900249 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900250 break;
251 case DHCPREQUEST:
252 log.trace("DHCP REQUEST received from {}", clientMac);
253 Ethernet requestReply = buildReply(
254 ethPacket,
255 (byte) DHCPACK.getValue(),
256 reqInstPort);
257 sendReply(context, requestReply);
258 log.trace("DHCP ACK({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900259 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900260 break;
261 case DHCPRELEASE:
262 log.trace("DHCP RELEASE received from {}", clientMac);
263 // do nothing
264 break;
265 default:
266 break;
267 }
268 }
269
Yi Tsengc7403c22017-06-19 16:23:22 -0700270 private DHCP.MsgType getPacketType(DHCP dhcpPacket) {
271 DhcpOption optType = dhcpPacket.getOption(OptionCode_MessageType);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900272 if (optType == null) {
273 log.trace("DHCP packet with no message type, ignore it");
274 return null;
275 }
276
Yi Tsengc7403c22017-06-19 16:23:22 -0700277 DHCP.MsgType inPacketType = DHCP.MsgType.getType(optType.getData()[0]);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900278 if (inPacketType == null) {
279 log.trace("DHCP packet with no packet type, ignore it");
280 }
281 return inPacketType;
282 }
283
284 private Ethernet buildReply(Ethernet ethRequest, byte packetType,
285 InstancePort reqInstPort) {
Daniel Parke0945c12018-08-28 17:22:25 +0900286 log.trace("Build for DHCP reply msg for instance port {}", reqInstPort.toString());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900287 Port osPort = osNetworkService.port(reqInstPort.portId());
Daniel Parke0945c12018-08-28 17:22:25 +0900288 if (osPort == null) {
289 log.error("Failed to retrieve openstack port information for instance port {}",
290 reqInstPort.toString());
Ray Milkey92815932018-08-28 10:32:17 -0700291 return null;
Daniel Parke0945c12018-08-28 17:22:25 +0900292 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900293 // pick one IP address to make a reply
294 IP fixedIp = osPort.getFixedIps().stream().findFirst().get();
295 Subnet osSubnet = osNetworkService.subnet(fixedIp.getSubnetId());
296
297 Ethernet ethReply = new Ethernet();
298 ethReply.setSourceMACAddress(dhcpServerMac);
299 ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
300 ethReply.setEtherType(Ethernet.TYPE_IPV4);
301
302 IPv4 ipv4Request = (IPv4) ethRequest.getPayload();
303 IPv4 ipv4Reply = new IPv4();
daniel park15506e82018-04-04 18:52:16 +0900304
305 ipv4Reply.setSourceAddress(clusterService.getLocalNode().ip().getIp4Address().toString());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900306 ipv4Reply.setDestinationAddress(reqInstPort.ipAddress().getIp4Address().toInt());
307 ipv4Reply.setTtl(PACKET_TTL);
308
309 UDP udpRequest = (UDP) ipv4Request.getPayload();
310 UDP udpReply = new UDP();
311 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
312 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
313
314 DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
315 DHCP dhcpReply = buildDhcpReply(
316 dhcpRequest,
317 packetType,
318 reqInstPort.ipAddress().getIp4Address(),
319 osSubnet);
320
321 udpReply.setPayload(dhcpReply);
322 ipv4Reply.setPayload(udpReply);
323 ethReply.setPayload(ipv4Reply);
324
325 return ethReply;
326 }
327
328 private void sendReply(PacketContext context, Ethernet ethReply) {
329 if (ethReply == null) {
330 return;
331 }
332 ConnectPoint srcPoint = context.inPacket().receivedFrom();
333 TrafficTreatment treatment = DefaultTrafficTreatment
334 .builder()
335 .setOutput(srcPoint.port())
336 .build();
337
338 packetService.emit(new DefaultOutboundPacket(
339 srcPoint.deviceId(),
340 treatment,
341 ByteBuffer.wrap(ethReply.serialize())));
342 context.block();
343 }
344
345 private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
346 Subnet osSubnet) {
daniel park15506e82018-04-04 18:52:16 +0900347 Ip4Address gatewayIp = clusterService.getLocalNode().ip().getIp4Address();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900348 int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
349
350 DHCP dhcpReply = new DHCP();
351 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
352 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
353 dhcpReply.setHardwareAddressLength((byte) 6);
354 dhcpReply.setTransactionId(request.getTransactionId());
355 dhcpReply.setFlags(request.getFlags());
356 dhcpReply.setYourIPAddress(yourIp.toInt());
357 dhcpReply.setServerIPAddress(gatewayIp.toInt());
358 dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress());
359
Yi Tsengc7403c22017-06-19 16:23:22 -0700360 List<DhcpOption> options = Lists.newArrayList();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 // message type
Yi Tsengc7403c22017-06-19 16:23:22 -0700362 DhcpOption option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900363 option.setCode(OptionCode_MessageType.getValue());
364 option.setLength((byte) 1);
365 byte[] optionData = {msgType};
366 option.setData(optionData);
367 options.add(option);
368
369 // server identifier
Yi Tsengc7403c22017-06-19 16:23:22 -0700370 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900371 option.setCode(OptionCode_DHCPServerIp.getValue());
372 option.setLength((byte) 4);
373 option.setData(gatewayIp.toOctets());
374 options.add(option);
375
376 // lease time
Yi Tsengc7403c22017-06-19 16:23:22 -0700377 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900378 option.setCode(OptionCode_LeaseTime.getValue());
379 option.setLength((byte) 4);
380 option.setData(DHCP_DATA_LEASE_INFINITE);
381 options.add(option);
382
383 // subnet mask
384 Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700385 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900386 option.setCode(OptionCode_SubnetMask.getValue());
387 option.setLength((byte) 4);
388 option.setData(subnetMask.toOctets());
389 options.add(option);
390
391 // broadcast address
392 Ip4Address broadcast = Ip4Address.makeMaskedAddress(yourIp, subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700393 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900394 option.setCode(OptionCode_BroadcastAddress.getValue());
395 option.setLength((byte) 4);
396 option.setData(broadcast.toOctets());
397 options.add(option);
398
399 // domain server
Yi Tsengc7403c22017-06-19 16:23:22 -0700400 option = new DhcpOption();
Daniel Park4d421002018-07-27 23:36:57 +0900401
402 List<String> dnsServers = osSubnet.getDnsNames();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900403 option.setCode(OptionCode_DomainServer.getValue());
Daniel Park4d421002018-07-27 23:36:57 +0900404
405 if (dnsServers.isEmpty()) {
406 option.setLength((byte) 8);
407 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
408 dnsByteBuf.put(DEFAULT_PRIMARY_DNS.toOctets());
409 dnsByteBuf.put(DEFAULT_SECONDARY_DNS.toOctets());
410
411 option.setData(dnsByteBuf.array());
412 } else {
413 int dnsLength = 4 * dnsServers.size();
414
415 option.setLength((byte) dnsLength);
416
417 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
418
419 for (int i = 0; i < dnsServers.size(); i++) {
420 dnsByteBuf.put(IpAddress.valueOf(dnsServers.get(i)).toOctets());
421 }
422 option.setData(dnsByteBuf.array());
423 }
424
Hyunsun Moon44aac662017-02-18 02:07:01 +0900425 options.add(option);
426
Yi Tsengc7403c22017-06-19 16:23:22 -0700427 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900428 option.setCode(DHCP_OPTION_MTU);
429 option.setLength((byte) 2);
Daniel Park468e7852018-07-28 00:38:45 +0900430 Network osNetwork = osNetworkService.network(osSubnet.getNetworkId());
431 checkNotNull(osNetwork);
432 checkNotNull(osNetwork.getMTU());
433
434 option.setData(ByteBuffer.allocate(2).putShort(osNetwork.getMTU().shortValue()).array());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900435 options.add(option);
436
Daniel Parkd9d4c292018-06-26 20:33:58 +0900437 // classless static route
438 if (!osSubnet.getHostRoutes().isEmpty()) {
439 option = new DhcpOption();
440 option.setCode(OptionCode_Classless_Static_Route.getValue());
441
442 int hostRoutesSize = hostRoutesSize(ImmutableList.copyOf(osSubnet.getHostRoutes()));
443 if (hostRoutesSize == 0) {
444 throw new IllegalArgumentException("Illegal CIDR hostRoutesSize value!");
445 }
446
447 log.trace("hostRouteSize: {}", hostRoutesSize);
448
449 option.setLength((byte) hostRoutesSize);
450 ByteBuffer hostRouteByteBuf = ByteBuffer.allocate(hostRoutesSize);
451
452 osSubnet.getHostRoutes().forEach(h -> {
453 log.debug("processing host route information: {}", h.toString());
454
455 IpPrefix ipPrefix = IpPrefix.valueOf(h.getDestination());
456
457 hostRouteByteBuf.put(bytesDestinationDescriptor(ipPrefix));
458
459 hostRouteByteBuf.put(Ip4Address.valueOf(h.getNexthop()).toOctets());
460 });
461
462 option.setData(hostRouteByteBuf.array());
463
464 options.add(option);
465 }
466
Daniel Park48f10332018-08-02 10:57:27 +0900467 // Sets the default router address up.
468 // Performs only if the gateway is set in subnet.
469 if (!Strings.isNullOrEmpty(osSubnet.getGateway())) {
470 option = new DhcpOption();
471 option.setCode(OptionCode_RouterAddress.getValue());
472 option.setLength((byte) 4);
473 option.setData(Ip4Address.valueOf(osSubnet.getGateway()).toOctets());
474 options.add(option);
475 }
476
Hyunsun Moon44aac662017-02-18 02:07:01 +0900477 // end option
Yi Tsengc7403c22017-06-19 16:23:22 -0700478 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900479 option.setCode(OptionCode_END.getValue());
480 option.setLength((byte) 1);
481 options.add(option);
482
483 dhcpReply.setOptions(options);
484 return dhcpReply;
485 }
Daniel Parkd9d4c292018-06-26 20:33:58 +0900486
487 private int hostRoutesSize(List<HostRoute> hostRoutes) {
488 int size = 0;
489 int preFixLen;
490
491 for (HostRoute h : hostRoutes) {
492 preFixLen = IpPrefix.valueOf(h.getDestination()).prefixLength();
493 if (Math.max(V4_CIDR_LOWER_BOUND, preFixLen) == V4_CIDR_LOWER_BOUND ||
494 Math.min(preFixLen, V4_CIDR_UPPER_BOUND) == V4_CIDR_UPPER_BOUND) {
495 throw new IllegalArgumentException("Illegal CIDR length value!");
496 }
497
Daniel Parkf8e422d2018-07-30 14:14:37 +0900498 for (int i = 0; i <= V4_BYTE_SIZE; i++) {
Daniel Parkd9d4c292018-06-26 20:33:58 +0900499 if (preFixLen == Math.min(preFixLen, i * OCTET_BIT_LENGTH)) {
500 size = size + i + 1 + PADDING_SIZE;
501 break;
502 }
503 }
504 }
505 return size;
506 }
507
508 private byte[] bytesDestinationDescriptor(IpPrefix ipPrefix) {
509 ByteBuffer byteBuffer;
510 int prefixLen = ipPrefix.prefixLength();
511
512 // retrieve ipPrefix to the destination descriptor format
513 // ex) 10.1.1.0/24 -> [10,1,1,0]
514 String[] ipPrefixString = ipPrefix.getIp4Prefix().toString()
515 .split("/")[0]
516 .split("\\.");
517
Daniel Parkf8e422d2018-07-30 14:14:37 +0900518 // retrieve destination descriptor and put this to bytebuffer according to RFC 3442
519 // ex) 0.0.0.0/0 -> 0
Daniel Parkd9d4c292018-06-26 20:33:58 +0900520 // ex) 10.0.0.0/8 -> 8.10
521 // ex) 10.17.0.0/16 -> 16.10.17
522 // ex) 10.27.129.0/24 -> 24.10.27.129
523 // ex) 10.229.0.128/25 -> 25.10.229.0.128
Daniel Parkf8e422d2018-07-30 14:14:37 +0900524 for (int i = 0; i <= V4_BYTE_SIZE; i++) {
Daniel Parkd9d4c292018-06-26 20:33:58 +0900525 if (prefixLen == Math.min(prefixLen, i * OCTET_BIT_LENGTH)) {
526 byteBuffer = ByteBuffer.allocate(i + 1);
527 byteBuffer.put((byte) prefixLen);
528
529 for (int j = 0; j < i; j++) {
530 byteBuffer.put((byte) Integer.parseInt(ipPrefixString[j]));
531 }
532 return byteBuffer.array();
533 }
534 }
535
536 return null;
537 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900538 }
daniel park15506e82018-04-04 18:52:16 +0900539
540 private class InternalNodeEventListener implements OpenstackNodeListener {
541 @Override
542 public boolean isRelevant(OpenstackNodeEvent event) {
543 // do not allow to proceed without leadership
544 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900545 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
daniel park15506e82018-04-04 18:52:16 +0900546 }
547
548 @Override
549 public void event(OpenstackNodeEvent event) {
550 OpenstackNode osNode = event.subject();
551 switch (event.type()) {
552 case OPENSTACK_NODE_COMPLETE:
553 setDhcpRule(osNode, true);
554 break;
555 case OPENSTACK_NODE_INCOMPLETE:
556 setDhcpRule(osNode, false);
557 break;
558 case OPENSTACK_NODE_CREATED:
559 case OPENSTACK_NODE_UPDATED:
560 case OPENSTACK_NODE_REMOVED:
561 default:
562 break;
563 }
564 }
565
566 private void setDhcpRule(OpenstackNode openstackNode, boolean install) {
daniel park15506e82018-04-04 18:52:16 +0900567 TrafficSelector selector = DefaultTrafficSelector.builder()
568 .matchEthType(Ethernet.TYPE_IPV4)
569 .matchIPProtocol(IPv4.PROTOCOL_UDP)
570 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
571 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
572 .build();
573
574 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
575 .punt()
576 .build();
577
578 osFlowRuleService.setRule(
579 appId,
580 openstackNode.intgBridge(),
581 selector,
582 treatment,
583 PRIORITY_DHCP_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900584 DHCP_TABLE,
daniel park15506e82018-04-04 18:52:16 +0900585 install);
586 }
587 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900588}