blob: 0b751e6a2b4db30dd0ae56f97349f64ce5c5b275 [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;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.onlab.packet.DHCP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090029import org.onlab.packet.Ethernet;
30import org.onlab.packet.IPv4;
31import org.onlab.packet.Ip4Address;
Daniel Park4d421002018-07-27 23:36:57 +090032import org.onlab.packet.IpAddress;
Hyunsun Moon44aac662017-02-18 02:07:01 +090033import org.onlab.packet.IpPrefix;
34import org.onlab.packet.MacAddress;
35import org.onlab.packet.TpPort;
36import org.onlab.packet.UDP;
daniel park796c2eb2018-03-22 17:01:51 +090037import org.onlab.packet.dhcp.DhcpOption;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import org.onlab.util.Tools;
39import org.onosproject.cfg.ComponentConfigService;
daniel park15506e82018-04-04 18:52:16 +090040import org.onosproject.cluster.ClusterService;
41import org.onosproject.cluster.LeadershipService;
42import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090043import org.onosproject.core.ApplicationId;
44import org.onosproject.core.CoreService;
45import org.onosproject.net.ConnectPoint;
46import org.onosproject.net.flow.DefaultTrafficSelector;
47import org.onosproject.net.flow.DefaultTrafficTreatment;
48import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
50import org.onosproject.net.packet.DefaultOutboundPacket;
51import org.onosproject.net.packet.PacketContext;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.onosproject.net.packet.PacketProcessor;
53import org.onosproject.net.packet.PacketService;
54import org.onosproject.openstacknetworking.api.Constants;
55import org.onosproject.openstacknetworking.api.InstancePort;
56import org.onosproject.openstacknetworking.api.InstancePortService;
daniel park796c2eb2018-03-22 17:01:51 +090057import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090058import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
daniel park15506e82018-04-04 18:52:16 +090059import org.onosproject.openstacknode.api.OpenstackNode;
60import org.onosproject.openstacknode.api.OpenstackNodeEvent;
61import org.onosproject.openstacknode.api.OpenstackNodeListener;
daniel park796c2eb2018-03-22 17:01:51 +090062import org.onosproject.openstacknode.api.OpenstackNodeService;
Daniel Parkd9d4c292018-06-26 20:33:58 +090063import org.openstack4j.model.network.HostRoute;
Hyunsun Moon44aac662017-02-18 02:07:01 +090064import org.openstack4j.model.network.IP;
65import org.openstack4j.model.network.Port;
66import org.openstack4j.model.network.Subnet;
67import org.osgi.service.component.ComponentContext;
68import 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 park796c2eb2018-03-22 17:01:51 +090075import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_BroadcastAddress;
Daniel Parkd9d4c292018-06-26 20:33:58 +090076import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_Classless_Static_Route;
daniel park796c2eb2018-03-22 17:01:51 +090077import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
78import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DomainServer;
79import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
80import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_LeaseTime;
81import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
82import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RouterAddress;
83import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_SubnetMask;
Yi Tsengc7403c22017-06-19 16:23:22 -070084import static org.onlab.packet.DHCP.MsgType.DHCPACK;
85import static org.onlab.packet.DHCP.MsgType.DHCPOFFER;
Hyunsun Moon44aac662017-02-18 02:07:01 +090086import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Li70a2c3f2018-04-13 17:26:31 +090087import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
daniel park796c2eb2018-03-22 17:01:51 +090088import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
Jian Li51b844c2018-05-31 10:59:03 +090089import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090090import static org.slf4j.LoggerFactory.getLogger;
91
92/**
93 * Handles DHCP requests for the virtual instances.
94 */
95@Component(immediate = true)
96public class OpenstackSwitchingDhcpHandler {
97 protected final Logger log = getLogger(getClass());
98
99 private static final String DHCP_SERVER_MAC = "dhcpServerMac";
Jian Lifb005492018-03-02 10:50:15 +0900100 private static final String DHCP_DATA_MTU = "dhcpDataMtu";
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;
112 private static final int V4_CIDR_LOWER_BOUND = 0;
113 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
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected CoreService coreService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected ComponentConfigService configService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected PacketService packetService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected InstancePortService instancePortService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected OpenstackNetworkService osNetworkService;
130
daniel park796c2eb2018-03-22 17:01:51 +0900131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected OpenstackNodeService osNodeService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected OpenstackFlowRuleService osFlowRuleService;
136
daniel park15506e82018-04-04 18:52:16 +0900137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected ClusterService clusterService;
139
140 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
141 protected LeadershipService leadershipService;
142
Hyunsun Moon44aac662017-02-18 02:07:01 +0900143 @Property(name = DHCP_SERVER_MAC, value = DEFAULT_GATEWAY_MAC_STR,
144 label = "Fake MAC address for virtual network subnet gateway")
145 private String dhcpServerMac = DEFAULT_GATEWAY_MAC_STR;
146
Jian Lifb005492018-03-02 10:50:15 +0900147 @Property(name = DHCP_DATA_MTU, intValue = DHCP_DATA_MTU_DEFAULT,
148 label = "DHCP data Maximum Transmission Unit")
149 private int dhcpDataMtu = DHCP_DATA_MTU_DEFAULT;
150
Hyunsun Moon44aac662017-02-18 02:07:01 +0900151 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
daniel park15506e82018-04-04 18:52:16 +0900152 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900153
154 private ApplicationId appId;
daniel park15506e82018-04-04 18:52:16 +0900155 private NodeId localNodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900156
157 @Activate
158 protected void activate() {
159 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
daniel park15506e82018-04-04 18:52:16 +0900160 localNodeId = clusterService.getLocalNode().id();
161 osNodeService.addListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900162 configService.registerProperties(getClass());
163 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
daniel park15506e82018-04-04 18:52:16 +0900164 leadershipService.runForLeadership(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900165
166 log.info("Started");
167 }
168
169 @Deactivate
170 protected void deactivate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900171 packetService.removeProcessor(packetProcessor);
daniel park15506e82018-04-04 18:52:16 +0900172 osNodeService.removeListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900173 configService.unregisterProperties(getClass(), false);
daniel park15506e82018-04-04 18:52:16 +0900174 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900175
176 log.info("Stopped");
177 }
178
179 @Modified
180 protected void modified(ComponentContext context) {
181 Dictionary<?, ?> properties = context.getProperties();
182 String updatedMac;
Jian Lifb005492018-03-02 10:50:15 +0900183 Integer updateMtu;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184
185 updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
Jian Lifb005492018-03-02 10:50:15 +0900186 updateMtu = Tools.getIntegerProperty(properties, DHCP_DATA_MTU);
187
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188 if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
189 dhcpServerMac = updatedMac;
190 }
191
Jian Lifb005492018-03-02 10:50:15 +0900192 if (updateMtu != null && updateMtu != dhcpDataMtu) {
193 dhcpDataMtu = updateMtu;
194 }
195
Hyunsun Moon44aac662017-02-18 02:07:01 +0900196 log.info("Modified");
197 }
198
Hyunsun Moon44aac662017-02-18 02:07:01 +0900199 private class InternalPacketProcessor implements PacketProcessor {
200
201 @Override
202 public void process(PacketContext context) {
203 if (context.isHandled()) {
204 return;
205 }
206
207 Ethernet ethPacket = context.inPacket().parsed();
208 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) {
209 return;
210 }
211 IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
212 if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_UDP) {
213 return;
214 }
215 UDP udpPacket = (UDP) ipv4Packet.getPayload();
216 if (udpPacket.getDestinationPort() != UDP.DHCP_SERVER_PORT ||
217 udpPacket.getSourcePort() != UDP.DHCP_CLIENT_PORT) {
218 return;
219 }
220
221 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
222 processDhcp(context, dhcpPacket);
223 }
224
225 private void processDhcp(PacketContext context, DHCP dhcpPacket) {
226 if (dhcpPacket == null) {
227 log.trace("DHCP packet without payload received, do nothing");
228 return;
229 }
230
Yi Tsengc7403c22017-06-19 16:23:22 -0700231 DHCP.MsgType inPacketType = getPacketType(dhcpPacket);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900232 if (inPacketType == null || dhcpPacket.getClientHardwareAddress() == null) {
233 log.trace("Malformed DHCP packet received, ignore it");
234 return;
235 }
236
237 MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
238 InstancePort reqInstPort = instancePortService.instancePort(clientMac);
239 if (reqInstPort == null) {
240 log.trace("Failed to find host(MAC:{})", clientMac);
241 return;
242 }
243 Ethernet ethPacket = context.inPacket().parsed();
244 switch (inPacketType) {
245 case DHCPDISCOVER:
246 log.trace("DHCP DISCOVER received from {}", clientMac);
247 Ethernet discoverReply = buildReply(
248 ethPacket,
249 (byte) DHCPOFFER.getValue(),
250 reqInstPort);
251 sendReply(context, discoverReply);
252 log.trace("DHCP OFFER({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900253 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900254 break;
255 case DHCPREQUEST:
256 log.trace("DHCP REQUEST received from {}", clientMac);
257 Ethernet requestReply = buildReply(
258 ethPacket,
259 (byte) DHCPACK.getValue(),
260 reqInstPort);
261 sendReply(context, requestReply);
262 log.trace("DHCP ACK({}) is sent for {}",
daniel park15506e82018-04-04 18:52:16 +0900263 reqInstPort.ipAddress(), clientMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900264 break;
265 case DHCPRELEASE:
266 log.trace("DHCP RELEASE received from {}", clientMac);
267 // do nothing
268 break;
269 default:
270 break;
271 }
272 }
273
Yi Tsengc7403c22017-06-19 16:23:22 -0700274 private DHCP.MsgType getPacketType(DHCP dhcpPacket) {
275 DhcpOption optType = dhcpPacket.getOption(OptionCode_MessageType);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900276 if (optType == null) {
277 log.trace("DHCP packet with no message type, ignore it");
278 return null;
279 }
280
Yi Tsengc7403c22017-06-19 16:23:22 -0700281 DHCP.MsgType inPacketType = DHCP.MsgType.getType(optType.getData()[0]);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900282 if (inPacketType == null) {
283 log.trace("DHCP packet with no packet type, ignore it");
284 }
285 return inPacketType;
286 }
287
288 private Ethernet buildReply(Ethernet ethRequest, byte packetType,
289 InstancePort reqInstPort) {
290 Port osPort = osNetworkService.port(reqInstPort.portId());
291 // pick one IP address to make a reply
292 IP fixedIp = osPort.getFixedIps().stream().findFirst().get();
293 Subnet osSubnet = osNetworkService.subnet(fixedIp.getSubnetId());
294
295 Ethernet ethReply = new Ethernet();
296 ethReply.setSourceMACAddress(dhcpServerMac);
297 ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
298 ethReply.setEtherType(Ethernet.TYPE_IPV4);
299
300 IPv4 ipv4Request = (IPv4) ethRequest.getPayload();
301 IPv4 ipv4Reply = new IPv4();
daniel park15506e82018-04-04 18:52:16 +0900302
303 ipv4Reply.setSourceAddress(clusterService.getLocalNode().ip().getIp4Address().toString());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900304 ipv4Reply.setDestinationAddress(reqInstPort.ipAddress().getIp4Address().toInt());
305 ipv4Reply.setTtl(PACKET_TTL);
306
307 UDP udpRequest = (UDP) ipv4Request.getPayload();
308 UDP udpReply = new UDP();
309 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
310 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
311
312 DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
313 DHCP dhcpReply = buildDhcpReply(
314 dhcpRequest,
315 packetType,
316 reqInstPort.ipAddress().getIp4Address(),
317 osSubnet);
318
319 udpReply.setPayload(dhcpReply);
320 ipv4Reply.setPayload(udpReply);
321 ethReply.setPayload(ipv4Reply);
322
323 return ethReply;
324 }
325
326 private void sendReply(PacketContext context, Ethernet ethReply) {
327 if (ethReply == null) {
328 return;
329 }
330 ConnectPoint srcPoint = context.inPacket().receivedFrom();
331 TrafficTreatment treatment = DefaultTrafficTreatment
332 .builder()
333 .setOutput(srcPoint.port())
334 .build();
335
336 packetService.emit(new DefaultOutboundPacket(
337 srcPoint.deviceId(),
338 treatment,
339 ByteBuffer.wrap(ethReply.serialize())));
340 context.block();
341 }
342
343 private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
344 Subnet osSubnet) {
daniel park15506e82018-04-04 18:52:16 +0900345 Ip4Address gatewayIp = clusterService.getLocalNode().ip().getIp4Address();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900346 int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
347
348 DHCP dhcpReply = new DHCP();
349 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
350 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
351 dhcpReply.setHardwareAddressLength((byte) 6);
352 dhcpReply.setTransactionId(request.getTransactionId());
353 dhcpReply.setFlags(request.getFlags());
354 dhcpReply.setYourIPAddress(yourIp.toInt());
355 dhcpReply.setServerIPAddress(gatewayIp.toInt());
356 dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress());
357
Yi Tsengc7403c22017-06-19 16:23:22 -0700358 List<DhcpOption> options = Lists.newArrayList();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900359 // message type
Yi Tsengc7403c22017-06-19 16:23:22 -0700360 DhcpOption option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 option.setCode(OptionCode_MessageType.getValue());
362 option.setLength((byte) 1);
363 byte[] optionData = {msgType};
364 option.setData(optionData);
365 options.add(option);
366
367 // server identifier
Yi Tsengc7403c22017-06-19 16:23:22 -0700368 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900369 option.setCode(OptionCode_DHCPServerIp.getValue());
370 option.setLength((byte) 4);
371 option.setData(gatewayIp.toOctets());
372 options.add(option);
373
374 // lease time
Yi Tsengc7403c22017-06-19 16:23:22 -0700375 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900376 option.setCode(OptionCode_LeaseTime.getValue());
377 option.setLength((byte) 4);
378 option.setData(DHCP_DATA_LEASE_INFINITE);
379 options.add(option);
380
381 // subnet mask
382 Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700383 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900384 option.setCode(OptionCode_SubnetMask.getValue());
385 option.setLength((byte) 4);
386 option.setData(subnetMask.toOctets());
387 options.add(option);
388
389 // broadcast address
390 Ip4Address broadcast = Ip4Address.makeMaskedAddress(yourIp, subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700391 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900392 option.setCode(OptionCode_BroadcastAddress.getValue());
393 option.setLength((byte) 4);
394 option.setData(broadcast.toOctets());
395 options.add(option);
396
397 // domain server
Yi Tsengc7403c22017-06-19 16:23:22 -0700398 option = new DhcpOption();
Daniel Park4d421002018-07-27 23:36:57 +0900399
400 List<String> dnsServers = osSubnet.getDnsNames();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900401 option.setCode(OptionCode_DomainServer.getValue());
Daniel Park4d421002018-07-27 23:36:57 +0900402
403 if (dnsServers.isEmpty()) {
404 option.setLength((byte) 8);
405 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
406 dnsByteBuf.put(DEFAULT_PRIMARY_DNS.toOctets());
407 dnsByteBuf.put(DEFAULT_SECONDARY_DNS.toOctets());
408
409 option.setData(dnsByteBuf.array());
410 } else {
411 int dnsLength = 4 * dnsServers.size();
412
413 option.setLength((byte) dnsLength);
414
415 ByteBuffer dnsByteBuf = ByteBuffer.allocate(8);
416
417 for (int i = 0; i < dnsServers.size(); i++) {
418 dnsByteBuf.put(IpAddress.valueOf(dnsServers.get(i)).toOctets());
419 }
420 option.setData(dnsByteBuf.array());
421 }
422
Hyunsun Moon44aac662017-02-18 02:07:01 +0900423 options.add(option);
424
Yi Tsengc7403c22017-06-19 16:23:22 -0700425 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900426 option.setCode(DHCP_OPTION_MTU);
427 option.setLength((byte) 2);
Jian Lifb005492018-03-02 10:50:15 +0900428 option.setData(ByteBuffer.allocate(2).putShort((short) dhcpDataMtu).array());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900429 options.add(option);
430
Daniel Parkd9d4c292018-06-26 20:33:58 +0900431 // classless static route
432 if (!osSubnet.getHostRoutes().isEmpty()) {
433 option = new DhcpOption();
434 option.setCode(OptionCode_Classless_Static_Route.getValue());
435
436 int hostRoutesSize = hostRoutesSize(ImmutableList.copyOf(osSubnet.getHostRoutes()));
437 if (hostRoutesSize == 0) {
438 throw new IllegalArgumentException("Illegal CIDR hostRoutesSize value!");
439 }
440
441 log.trace("hostRouteSize: {}", hostRoutesSize);
442
443 option.setLength((byte) hostRoutesSize);
444 ByteBuffer hostRouteByteBuf = ByteBuffer.allocate(hostRoutesSize);
445
446 osSubnet.getHostRoutes().forEach(h -> {
447 log.debug("processing host route information: {}", h.toString());
448
449 IpPrefix ipPrefix = IpPrefix.valueOf(h.getDestination());
450
451 hostRouteByteBuf.put(bytesDestinationDescriptor(ipPrefix));
452
453 hostRouteByteBuf.put(Ip4Address.valueOf(h.getNexthop()).toOctets());
454 });
455
456 option.setData(hostRouteByteBuf.array());
457
458 options.add(option);
459 }
460
Hyunsun Moon44aac662017-02-18 02:07:01 +0900461 // router address
Yi Tsengc7403c22017-06-19 16:23:22 -0700462 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900463 option.setCode(OptionCode_RouterAddress.getValue());
464 option.setLength((byte) 4);
Jian Lib311c7e2018-05-15 13:47:19 +0900465 option.setData(Ip4Address.valueOf(osSubnet.getGateway()).toOctets());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900466 options.add(option);
467
468 // end option
Yi Tsengc7403c22017-06-19 16:23:22 -0700469 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900470 option.setCode(OptionCode_END.getValue());
471 option.setLength((byte) 1);
472 options.add(option);
473
474 dhcpReply.setOptions(options);
475 return dhcpReply;
476 }
Daniel Parkd9d4c292018-06-26 20:33:58 +0900477
478 private int hostRoutesSize(List<HostRoute> hostRoutes) {
479 int size = 0;
480 int preFixLen;
481
482 for (HostRoute h : hostRoutes) {
483 preFixLen = IpPrefix.valueOf(h.getDestination()).prefixLength();
484 if (Math.max(V4_CIDR_LOWER_BOUND, preFixLen) == V4_CIDR_LOWER_BOUND ||
485 Math.min(preFixLen, V4_CIDR_UPPER_BOUND) == V4_CIDR_UPPER_BOUND) {
486 throw new IllegalArgumentException("Illegal CIDR length value!");
487 }
488
489 for (int i = 1; i <= V4_BYTE_SIZE; i++) {
490
491 if (preFixLen == Math.min(preFixLen, i * OCTET_BIT_LENGTH)) {
492 size = size + i + 1 + PADDING_SIZE;
493 break;
494 }
495 }
496 }
497 return size;
498 }
499
500 private byte[] bytesDestinationDescriptor(IpPrefix ipPrefix) {
501 ByteBuffer byteBuffer;
502 int prefixLen = ipPrefix.prefixLength();
503
504 // retrieve ipPrefix to the destination descriptor format
505 // ex) 10.1.1.0/24 -> [10,1,1,0]
506 String[] ipPrefixString = ipPrefix.getIp4Prefix().toString()
507 .split("/")[0]
508 .split("\\.");
509
510 // retrieve destination descriptor and put this to bytebuffer according to 3442
511 // ex) 10.0.0.0/8 -> 8.10
512 // ex) 10.17.0.0/16 -> 16.10.17
513 // ex) 10.27.129.0/24 -> 24.10.27.129
514 // ex) 10.229.0.128/25 -> 25.10.229.0.128
515 for (int i = 1; i <= V4_BYTE_SIZE; i++) {
516 if (prefixLen == Math.min(prefixLen, i * OCTET_BIT_LENGTH)) {
517 byteBuffer = ByteBuffer.allocate(i + 1);
518 byteBuffer.put((byte) prefixLen);
519
520 for (int j = 0; j < i; j++) {
521 byteBuffer.put((byte) Integer.parseInt(ipPrefixString[j]));
522 }
523 return byteBuffer.array();
524 }
525 }
526
527 return null;
528 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900529 }
daniel park15506e82018-04-04 18:52:16 +0900530
531 private class InternalNodeEventListener implements OpenstackNodeListener {
532 @Override
533 public boolean isRelevant(OpenstackNodeEvent event) {
534 // do not allow to proceed without leadership
535 NodeId leader = leadershipService.getLeader(appId.name());
Jian Li51b844c2018-05-31 10:59:03 +0900536 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
daniel park15506e82018-04-04 18:52:16 +0900537 }
538
539 @Override
540 public void event(OpenstackNodeEvent event) {
541 OpenstackNode osNode = event.subject();
542 switch (event.type()) {
543 case OPENSTACK_NODE_COMPLETE:
544 setDhcpRule(osNode, true);
545 break;
546 case OPENSTACK_NODE_INCOMPLETE:
547 setDhcpRule(osNode, false);
548 break;
549 case OPENSTACK_NODE_CREATED:
550 case OPENSTACK_NODE_UPDATED:
551 case OPENSTACK_NODE_REMOVED:
552 default:
553 break;
554 }
555 }
556
557 private void setDhcpRule(OpenstackNode openstackNode, boolean install) {
daniel park15506e82018-04-04 18:52:16 +0900558 TrafficSelector selector = DefaultTrafficSelector.builder()
559 .matchEthType(Ethernet.TYPE_IPV4)
560 .matchIPProtocol(IPv4.PROTOCOL_UDP)
561 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
562 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
563 .build();
564
565 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
566 .punt()
567 .build();
568
569 osFlowRuleService.setRule(
570 appId,
571 openstackNode.intgBridge(),
572 selector,
573 treatment,
574 PRIORITY_DHCP_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900575 DHCP_ARP_TABLE,
daniel park15506e82018-04-04 18:52:16 +0900576 install);
577 }
578 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900579}