blob: efd10d9ae8fc207324be63860195047e6f7d2aea [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;
Hyunsun Moon44aac662017-02-18 02:07:01 +090087import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Li70a2c3f2018-04-13 17:26:31 +090088import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
daniel park796c2eb2018-03-22 17:01:51 +090089import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
Jian Li51b844c2018-05-31 10:59:03 +090090import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090091import static org.slf4j.LoggerFactory.getLogger;
92
93/**
94 * Handles DHCP requests for the virtual instances.
95 */
96@Component(immediate = true)
97public class OpenstackSwitchingDhcpHandler {
98 protected final Logger log = getLogger(getClass());
99
100 private static final String DHCP_SERVER_MAC = "dhcpServerMac";
Daniel Park4d421002018-07-27 23:36:57 +0900101 private static final Ip4Address DEFAULT_PRIMARY_DNS = Ip4Address.valueOf("8.8.8.8");
102 private static final Ip4Address DEFAULT_SECONDARY_DNS = Ip4Address.valueOf("8.8.4.4");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103 private static final byte PACKET_TTL = (byte) 127;
104 // TODO add MTU, static route option codes to ONOS DHCP and remove here
105 private static final byte DHCP_OPTION_MTU = (byte) 26;
106 private static final byte[] DHCP_DATA_LEASE_INFINITE =
107 ByteBuffer.allocate(4).putInt(-1).array();
Jian Lifb005492018-03-02 10:50:15 +0900108 // we are using 1450 as a default DHCP MTU value
109 private static final int DHCP_DATA_MTU_DEFAULT = 1450;
Daniel Parkd9d4c292018-06-26 20:33:58 +0900110 private static final int OCTET_BIT_LENGTH = 8;
111 private static final int V4_BYTE_SIZE = 4;
Daniel Parkf8e422d2018-07-30 14:14:37 +0900112 private static final int V4_CIDR_LOWER_BOUND = -1;
Daniel Parkd9d4c292018-06-26 20:33:58 +0900113 private static final int V4_CIDR_UPPER_BOUND = 33;
114 private static final int PADDING_SIZE = 4;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900115
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900117 protected CoreService coreService;
118
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900120 protected ComponentConfigService configService;
121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900123 protected PacketService packetService;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900126 protected InstancePortService instancePortService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129 protected OpenstackNetworkService osNetworkService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park796c2eb2018-03-22 17:01:51 +0900132 protected OpenstackNodeService osNodeService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park796c2eb2018-03-22 17:01:51 +0900135 protected OpenstackFlowRuleService osFlowRuleService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park15506e82018-04-04 18:52:16 +0900138 protected ClusterService clusterService;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park15506e82018-04-04 18:52:16 +0900141 protected LeadershipService leadershipService;
142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 //@Property(name = DHCP_SERVER_MAC, value = DEFAULT_GATEWAY_MAC_STR,
144 // label = "Fake MAC address for virtual network subnet gateway")
Hyunsun Moon44aac662017-02-18 02:07:01 +0900145 private String dhcpServerMac = DEFAULT_GATEWAY_MAC_STR;
146
Jian Lifb005492018-03-02 10:50:15 +0900147 private int dhcpDataMtu = DHCP_DATA_MTU_DEFAULT;
148
Hyunsun Moon44aac662017-02-18 02:07:01 +0900149 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
daniel park15506e82018-04-04 18:52:16 +0900150 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900151
152 private ApplicationId appId;
daniel park15506e82018-04-04 18:52:16 +0900153 private NodeId localNodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900154
155 @Activate
156 protected void activate() {
157 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
daniel park15506e82018-04-04 18:52:16 +0900158 localNodeId = clusterService.getLocalNode().id();
159 osNodeService.addListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900160 configService.registerProperties(getClass());
161 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
daniel park15506e82018-04-04 18:52:16 +0900162 leadershipService.runForLeadership(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900163
164 log.info("Started");
165 }
166
167 @Deactivate
168 protected void deactivate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 packetService.removeProcessor(packetProcessor);
daniel park15506e82018-04-04 18:52:16 +0900170 osNodeService.removeListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900171 configService.unregisterProperties(getClass(), false);
daniel park15506e82018-04-04 18:52:16 +0900172 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900173
174 log.info("Stopped");
175 }
176
177 @Modified
178 protected void modified(ComponentContext context) {
179 Dictionary<?, ?> properties = context.getProperties();
180 String updatedMac;
181
182 updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
Jian Lifb005492018-03-02 10:50:15 +0900183
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184 if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
185 dhcpServerMac = updatedMac;
186 }
187
188 log.info("Modified");
189 }
190
Hyunsun Moon44aac662017-02-18 02:07:01 +0900191 private class InternalPacketProcessor implements PacketProcessor {
192
193 @Override
194 public void process(PacketContext context) {
195 if (context.isHandled()) {
196 return;
197 }
198
199 Ethernet ethPacket = context.inPacket().parsed();
200 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) {
201 return;
202 }
203 IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
204 if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_UDP) {
205 return;
206 }
207 UDP udpPacket = (UDP) ipv4Packet.getPayload();
208 if (udpPacket.getDestinationPort() != UDP.DHCP_SERVER_PORT ||
209 udpPacket.getSourcePort() != UDP.DHCP_CLIENT_PORT) {
210 return;
211 }
212
213 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
214 processDhcp(context, dhcpPacket);
215 }
216
217 private void processDhcp(PacketContext context, DHCP dhcpPacket) {
218 if (dhcpPacket == null) {
219 log.trace("DHCP packet without payload received, do nothing");
220 return;
221 }
222
Yi Tsengc7403c22017-06-19 16:23:22 -0700223 DHCP.MsgType inPacketType = getPacketType(dhcpPacket);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900224 if (inPacketType == null || dhcpPacket.getClientHardwareAddress() == null) {
225 log.trace("Malformed DHCP packet received, ignore it");
226 return;
227 }
228
229 MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
230 InstancePort reqInstPort = instancePortService.instancePort(clientMac);
231 if (reqInstPort == null) {
232 log.trace("Failed to find host(MAC:{})", clientMac);
233 return;
234 }
235 Ethernet ethPacket = context.inPacket().parsed();
236 switch (inPacketType) {
237 case DHCPDISCOVER:
238 log.trace("DHCP DISCOVER received from {}", clientMac);
239 Ethernet discoverReply = buildReply(
240 ethPacket,
241 (byte) DHCPOFFER.getValue(),
242 reqInstPort);
243 sendReply(context, discoverReply);
244 log.trace("DHCP OFFER({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900245 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900246 break;
247 case DHCPREQUEST:
248 log.trace("DHCP REQUEST received from {}", clientMac);
249 Ethernet requestReply = buildReply(
250 ethPacket,
251 (byte) DHCPACK.getValue(),
252 reqInstPort);
253 sendReply(context, requestReply);
254 log.trace("DHCP ACK({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900255 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900256 break;
257 case DHCPRELEASE:
258 log.trace("DHCP RELEASE received from {}", clientMac);
259 // do nothing
260 break;
261 default:
262 break;
263 }
264 }
265
Yi Tsengc7403c22017-06-19 16:23:22 -0700266 private DHCP.MsgType getPacketType(DHCP dhcpPacket) {
267 DhcpOption optType = dhcpPacket.getOption(OptionCode_MessageType);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900268 if (optType == null) {
269 log.trace("DHCP packet with no message type, ignore it");
270 return null;
271 }
272
Yi Tsengc7403c22017-06-19 16:23:22 -0700273 DHCP.MsgType inPacketType = DHCP.MsgType.getType(optType.getData()[0]);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900274 if (inPacketType == null) {
275 log.trace("DHCP packet with no packet type, ignore it");
276 }
277 return inPacketType;
278 }
279
280 private Ethernet buildReply(Ethernet ethRequest, byte packetType,
281 InstancePort reqInstPort) {
Daniel Parke0945c12018-08-28 17:22:25 +0900282 log.trace("Build for DHCP reply msg for instance port {}", reqInstPort.toString());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900283 Port osPort = osNetworkService.port(reqInstPort.portId());
Daniel Parke0945c12018-08-28 17:22:25 +0900284 if (osPort == null) {
285 log.error("Failed to retrieve openstack port information for instance port {}",
286 reqInstPort.toString());
Ray Milkey92815932018-08-28 10:32:17 -0700287 return null;
Daniel Parke0945c12018-08-28 17:22:25 +0900288 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900289 // pick one IP address to make a reply
290 IP fixedIp = osPort.getFixedIps().stream().findFirst().get();
291 Subnet osSubnet = osNetworkService.subnet(fixedIp.getSubnetId());
292
293 Ethernet ethReply = new Ethernet();
294 ethReply.setSourceMACAddress(dhcpServerMac);
295 ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
296 ethReply.setEtherType(Ethernet.TYPE_IPV4);
297
298 IPv4 ipv4Request = (IPv4) ethRequest.getPayload();
299 IPv4 ipv4Reply = new IPv4();
daniel park15506e82018-04-04 18:52:16 +0900300
301 ipv4Reply.setSourceAddress(clusterService.getLocalNode().ip().getIp4Address().toString());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900302 ipv4Reply.setDestinationAddress(reqInstPort.ipAddress().getIp4Address().toInt());
303 ipv4Reply.setTtl(PACKET_TTL);
304
305 UDP udpRequest = (UDP) ipv4Request.getPayload();
306 UDP udpReply = new UDP();
307 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
308 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
309
310 DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
311 DHCP dhcpReply = buildDhcpReply(
312 dhcpRequest,
313 packetType,
314 reqInstPort.ipAddress().getIp4Address(),
315 osSubnet);
316
317 udpReply.setPayload(dhcpReply);
318 ipv4Reply.setPayload(udpReply);
319 ethReply.setPayload(ipv4Reply);
320
321 return ethReply;
322 }
323
324 private void sendReply(PacketContext context, Ethernet ethReply) {
325 if (ethReply == null) {
326 return;
327 }
328 ConnectPoint srcPoint = context.inPacket().receivedFrom();
329 TrafficTreatment treatment = DefaultTrafficTreatment
330 .builder()
331 .setOutput(srcPoint.port())
332 .build();
333
334 packetService.emit(new DefaultOutboundPacket(
335 srcPoint.deviceId(),
336 treatment,
337 ByteBuffer.wrap(ethReply.serialize())));
338 context.block();
339 }
340
341 private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
342 Subnet osSubnet) {
daniel park15506e82018-04-04 18:52:16 +0900343 Ip4Address gatewayIp = clusterService.getLocalNode().ip().getIp4Address();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900344 int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
345
346 DHCP dhcpReply = new DHCP();
347 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
348 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
349 dhcpReply.setHardwareAddressLength((byte) 6);
350 dhcpReply.setTransactionId(request.getTransactionId());
351 dhcpReply.setFlags(request.getFlags());
352 dhcpReply.setYourIPAddress(yourIp.toInt());
353 dhcpReply.setServerIPAddress(gatewayIp.toInt());
354 dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress());
355
Yi Tsengc7403c22017-06-19 16:23:22 -0700356 List<DhcpOption> options = Lists.newArrayList();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900357 // message type
Yi Tsengc7403c22017-06-19 16:23:22 -0700358 DhcpOption option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900359 option.setCode(OptionCode_MessageType.getValue());
360 option.setLength((byte) 1);
361 byte[] optionData = {msgType};
362 option.setData(optionData);
363 options.add(option);
364
365 // server identifier
Yi Tsengc7403c22017-06-19 16:23:22 -0700366 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900367 option.setCode(OptionCode_DHCPServerIp.getValue());
368 option.setLength((byte) 4);
369 option.setData(gatewayIp.toOctets());
370 options.add(option);
371
372 // lease time
Yi Tsengc7403c22017-06-19 16:23:22 -0700373 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900374 option.setCode(OptionCode_LeaseTime.getValue());
375 option.setLength((byte) 4);
376 option.setData(DHCP_DATA_LEASE_INFINITE);
377 options.add(option);
378
379 // subnet mask
380 Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700381 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900382 option.setCode(OptionCode_SubnetMask.getValue());
383 option.setLength((byte) 4);
384 option.setData(subnetMask.toOctets());
385 options.add(option);
386
387 // broadcast address
388 Ip4Address broadcast = Ip4Address.makeMaskedAddress(yourIp, subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700389 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900390 option.setCode(OptionCode_BroadcastAddress.getValue());
391 option.setLength((byte) 4);
392 option.setData(broadcast.toOctets());
393 options.add(option);
394
395 // domain server
Yi Tsengc7403c22017-06-19 16:23:22 -0700396 option = new DhcpOption();
Daniel Park4d421002018-07-27 23:36:57 +0900397
398 List<String> dnsServers = osSubnet.getDnsNames();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900399 option.setCode(OptionCode_DomainServer.getValue());
Daniel Park4d421002018-07-27 23:36:57 +0900400
401 if (dnsServers.isEmpty()) {
402 option.setLength((byte) 8);
403 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
404 dnsByteBuf.put(DEFAULT_PRIMARY_DNS.toOctets());
405 dnsByteBuf.put(DEFAULT_SECONDARY_DNS.toOctets());
406
407 option.setData(dnsByteBuf.array());
408 } else {
409 int dnsLength = 4 * dnsServers.size();
410
411 option.setLength((byte) dnsLength);
412
413 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
414
415 for (int i = 0; i < dnsServers.size(); i++) {
416 dnsByteBuf.put(IpAddress.valueOf(dnsServers.get(i)).toOctets());
417 }
418 option.setData(dnsByteBuf.array());
419 }
420
Hyunsun Moon44aac662017-02-18 02:07:01 +0900421 options.add(option);
422
Yi Tsengc7403c22017-06-19 16:23:22 -0700423 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900424 option.setCode(DHCP_OPTION_MTU);
425 option.setLength((byte) 2);
Daniel Park468e7852018-07-28 00:38:45 +0900426 Network osNetwork = osNetworkService.network(osSubnet.getNetworkId());
427 checkNotNull(osNetwork);
428 checkNotNull(osNetwork.getMTU());
429
430 option.setData(ByteBuffer.allocate(2).putShort(osNetwork.getMTU().shortValue()).array());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900431 options.add(option);
432
Daniel Parkd9d4c292018-06-26 20:33:58 +0900433 // classless static route
434 if (!osSubnet.getHostRoutes().isEmpty()) {
435 option = new DhcpOption();
436 option.setCode(OptionCode_Classless_Static_Route.getValue());
437
438 int hostRoutesSize = hostRoutesSize(ImmutableList.copyOf(osSubnet.getHostRoutes()));
439 if (hostRoutesSize == 0) {
440 throw new IllegalArgumentException("Illegal CIDR hostRoutesSize value!");
441 }
442
443 log.trace("hostRouteSize: {}", hostRoutesSize);
444
445 option.setLength((byte) hostRoutesSize);
446 ByteBuffer hostRouteByteBuf = ByteBuffer.allocate(hostRoutesSize);
447
448 osSubnet.getHostRoutes().forEach(h -> {
449 log.debug("processing host route information: {}", h.toString());
450
451 IpPrefix ipPrefix = IpPrefix.valueOf(h.getDestination());
452
453 hostRouteByteBuf.put(bytesDestinationDescriptor(ipPrefix));
454
455 hostRouteByteBuf.put(Ip4Address.valueOf(h.getNexthop()).toOctets());
456 });
457
458 option.setData(hostRouteByteBuf.array());
459
460 options.add(option);
461 }
462
Daniel Park48f10332018-08-02 10:57:27 +0900463 // Sets the default router address up.
464 // Performs only if the gateway is set in subnet.
465 if (!Strings.isNullOrEmpty(osSubnet.getGateway())) {
466 option = new DhcpOption();
467 option.setCode(OptionCode_RouterAddress.getValue());
468 option.setLength((byte) 4);
469 option.setData(Ip4Address.valueOf(osSubnet.getGateway()).toOctets());
470 options.add(option);
471 }
472
Hyunsun Moon44aac662017-02-18 02:07:01 +0900473 // end option
Yi Tsengc7403c22017-06-19 16:23:22 -0700474 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900475 option.setCode(OptionCode_END.getValue());
476 option.setLength((byte) 1);
477 options.add(option);
478
479 dhcpReply.setOptions(options);
480 return dhcpReply;
481 }
Daniel Parkd9d4c292018-06-26 20:33:58 +0900482
483 private int hostRoutesSize(List<HostRoute> hostRoutes) {
484 int size = 0;
485 int preFixLen;
486
487 for (HostRoute h : hostRoutes) {
488 preFixLen = IpPrefix.valueOf(h.getDestination()).prefixLength();
489 if (Math.max(V4_CIDR_LOWER_BOUND, preFixLen) == V4_CIDR_LOWER_BOUND ||
490 Math.min(preFixLen, V4_CIDR_UPPER_BOUND) == V4_CIDR_UPPER_BOUND) {
491 throw new IllegalArgumentException("Illegal CIDR length value!");
492 }
493
Daniel Parkf8e422d2018-07-30 14:14:37 +0900494 for (int i = 0; i <= V4_BYTE_SIZE; i++) {
Daniel Parkd9d4c292018-06-26 20:33:58 +0900495 if (preFixLen == Math.min(preFixLen, i * OCTET_BIT_LENGTH)) {
496 size = size + i + 1 + PADDING_SIZE;
497 break;
498 }
499 }
500 }
501 return size;
502 }
503
504 private byte[] bytesDestinationDescriptor(IpPrefix ipPrefix) {
505 ByteBuffer byteBuffer;
506 int prefixLen = ipPrefix.prefixLength();
507
508 // retrieve ipPrefix to the destination descriptor format
509 // ex) 10.1.1.0/24 -> [10,1,1,0]
510 String[] ipPrefixString = ipPrefix.getIp4Prefix().toString()
511 .split("/")[0]
512 .split("\\.");
513
Daniel Parkf8e422d2018-07-30 14:14:37 +0900514 // retrieve destination descriptor and put this to bytebuffer according to RFC 3442
515 // ex) 0.0.0.0/0 -> 0
Daniel Parkd9d4c292018-06-26 20:33:58 +0900516 // ex) 10.0.0.0/8 -> 8.10
517 // ex) 10.17.0.0/16 -> 16.10.17
518 // ex) 10.27.129.0/24 -> 24.10.27.129
519 // ex) 10.229.0.128/25 -> 25.10.229.0.128
Daniel Parkf8e422d2018-07-30 14:14:37 +0900520 for (int i = 0; i <= V4_BYTE_SIZE; i++) {
Daniel Parkd9d4c292018-06-26 20:33:58 +0900521 if (prefixLen == Math.min(prefixLen, i * OCTET_BIT_LENGTH)) {
522 byteBuffer = ByteBuffer.allocate(i + 1);
523 byteBuffer.put((byte) prefixLen);
524
525 for (int j = 0; j < i; j++) {
526 byteBuffer.put((byte) Integer.parseInt(ipPrefixString[j]));
527 }
528 return byteBuffer.array();
529 }
530 }
531
532 return null;
533 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900534 }
daniel park15506e82018-04-04 18:52:16 +0900535
536 private class InternalNodeEventListener implements OpenstackNodeListener {
537 @Override
538 public boolean isRelevant(OpenstackNodeEvent event) {
539 // do not allow to proceed without leadership
540 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900541 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
daniel park15506e82018-04-04 18:52:16 +0900542 }
543
544 @Override
545 public void event(OpenstackNodeEvent event) {
546 OpenstackNode osNode = event.subject();
547 switch (event.type()) {
548 case OPENSTACK_NODE_COMPLETE:
549 setDhcpRule(osNode, true);
550 break;
551 case OPENSTACK_NODE_INCOMPLETE:
552 setDhcpRule(osNode, false);
553 break;
554 case OPENSTACK_NODE_CREATED:
555 case OPENSTACK_NODE_UPDATED:
556 case OPENSTACK_NODE_REMOVED:
557 default:
558 break;
559 }
560 }
561
562 private void setDhcpRule(OpenstackNode openstackNode, boolean install) {
daniel park15506e82018-04-04 18:52:16 +0900563 TrafficSelector selector = DefaultTrafficSelector.builder()
564 .matchEthType(Ethernet.TYPE_IPV4)
565 .matchIPProtocol(IPv4.PROTOCOL_UDP)
566 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
567 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
568 .build();
569
570 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
571 .punt()
572 .build();
573
574 osFlowRuleService.setRule(
575 appId,
576 openstackNode.intgBridge(),
577 selector,
578 treatment,
579 PRIORITY_DHCP_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900580 DHCP_ARP_TABLE,
daniel park15506e82018-04-04 18:52:16 +0900581 install);
582 }
583 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900584}