blob: 3602275f614b252070823b0a7ed6b0c86494ce1e [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;
19import com.google.common.collect.Lists;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.DHCP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090028import org.onlab.packet.Ethernet;
29import org.onlab.packet.IPv4;
30import org.onlab.packet.Ip4Address;
31import org.onlab.packet.IpPrefix;
32import org.onlab.packet.MacAddress;
33import org.onlab.packet.TpPort;
34import org.onlab.packet.UDP;
daniel park796c2eb2018-03-22 17:01:51 +090035import org.onlab.packet.dhcp.DhcpOption;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onlab.util.Tools;
37import org.onosproject.cfg.ComponentConfigService;
38import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
40import org.onosproject.net.ConnectPoint;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.packet.DefaultOutboundPacket;
46import org.onosproject.net.packet.PacketContext;
Hyunsun Moon44aac662017-02-18 02:07:01 +090047import org.onosproject.net.packet.PacketProcessor;
48import org.onosproject.net.packet.PacketService;
49import org.onosproject.openstacknetworking.api.Constants;
50import org.onosproject.openstacknetworking.api.InstancePort;
51import org.onosproject.openstacknetworking.api.InstancePortService;
daniel park796c2eb2018-03-22 17:01:51 +090052import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090053import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
daniel park796c2eb2018-03-22 17:01:51 +090054import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090055import org.openstack4j.model.network.IP;
56import org.openstack4j.model.network.Port;
57import org.openstack4j.model.network.Subnet;
58import org.osgi.service.component.ComponentContext;
59import org.slf4j.Logger;
60
61import java.nio.ByteBuffer;
62import java.util.Dictionary;
63import java.util.List;
64
daniel park796c2eb2018-03-22 17:01:51 +090065import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_BroadcastAddress;
66import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
67import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DomainServer;
68import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
69import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_LeaseTime;
70import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
71import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RouterAddress;
72import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_SubnetMask;
Yi Tsengc7403c22017-06-19 16:23:22 -070073import static org.onlab.packet.DHCP.MsgType.DHCPACK;
74import static org.onlab.packet.DHCP.MsgType.DHCPOFFER;
Hyunsun Moon44aac662017-02-18 02:07:01 +090075import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
daniel park796c2eb2018-03-22 17:01:51 +090076import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
77import static org.onosproject.openstacknetworking.api.Constants.SRC_VNI_TABLE;
78import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090079import static org.slf4j.LoggerFactory.getLogger;
80
81/**
82 * Handles DHCP requests for the virtual instances.
83 */
84@Component(immediate = true)
85public class OpenstackSwitchingDhcpHandler {
86 protected final Logger log = getLogger(getClass());
87
88 private static final String DHCP_SERVER_MAC = "dhcpServerMac";
Jian Lifb005492018-03-02 10:50:15 +090089 private static final String DHCP_DATA_MTU = "dhcpDataMtu";
Hyunsun Moon44aac662017-02-18 02:07:01 +090090 private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
91 private static final byte PACKET_TTL = (byte) 127;
92 // TODO add MTU, static route option codes to ONOS DHCP and remove here
93 private static final byte DHCP_OPTION_MTU = (byte) 26;
94 private static final byte[] DHCP_DATA_LEASE_INFINITE =
95 ByteBuffer.allocate(4).putInt(-1).array();
Jian Lifb005492018-03-02 10:50:15 +090096 // we are using 1450 as a default DHCP MTU value
97 private static final int DHCP_DATA_MTU_DEFAULT = 1450;
Hyunsun Moon44aac662017-02-18 02:07:01 +090098
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected ComponentConfigService configService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected PacketService packetService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected InstancePortService instancePortService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected OpenstackNetworkService osNetworkService;
113
daniel park796c2eb2018-03-22 17:01:51 +0900114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected OpenstackNodeService osNodeService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected OpenstackFlowRuleService osFlowRuleService;
119
Hyunsun Moon44aac662017-02-18 02:07:01 +0900120 @Property(name = DHCP_SERVER_MAC, value = DEFAULT_GATEWAY_MAC_STR,
121 label = "Fake MAC address for virtual network subnet gateway")
122 private String dhcpServerMac = DEFAULT_GATEWAY_MAC_STR;
123
Jian Lifb005492018-03-02 10:50:15 +0900124 @Property(name = DHCP_DATA_MTU, intValue = DHCP_DATA_MTU_DEFAULT,
125 label = "DHCP data Maximum Transmission Unit")
126 private int dhcpDataMtu = DHCP_DATA_MTU_DEFAULT;
127
Hyunsun Moon44aac662017-02-18 02:07:01 +0900128 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
129
130 private ApplicationId appId;
131
132 @Activate
133 protected void activate() {
134 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
135 configService.registerProperties(getClass());
136 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
daniel park796c2eb2018-03-22 17:01:51 +0900137 setDhcpRule(true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900138
139 log.info("Started");
140 }
141
142 @Deactivate
143 protected void deactivate() {
daniel park796c2eb2018-03-22 17:01:51 +0900144 setDhcpRule(false);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900145 packetService.removeProcessor(packetProcessor);
146 configService.unregisterProperties(getClass(), false);
147
148 log.info("Stopped");
149 }
150
151 @Modified
152 protected void modified(ComponentContext context) {
153 Dictionary<?, ?> properties = context.getProperties();
154 String updatedMac;
Jian Lifb005492018-03-02 10:50:15 +0900155 Integer updateMtu;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900156
157 updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
Jian Lifb005492018-03-02 10:50:15 +0900158 updateMtu = Tools.getIntegerProperty(properties, DHCP_DATA_MTU);
159
Hyunsun Moon44aac662017-02-18 02:07:01 +0900160 if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
161 dhcpServerMac = updatedMac;
162 }
163
Jian Lifb005492018-03-02 10:50:15 +0900164 if (updateMtu != null && updateMtu != dhcpDataMtu) {
165 dhcpDataMtu = updateMtu;
166 }
167
Hyunsun Moon44aac662017-02-18 02:07:01 +0900168 log.info("Modified");
169 }
170
daniel park796c2eb2018-03-22 17:01:51 +0900171 private void setDhcpRule(boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900172 TrafficSelector selector = DefaultTrafficSelector.builder()
173 .matchEthType(Ethernet.TYPE_IPV4)
174 .matchIPProtocol(IPv4.PROTOCOL_UDP)
175 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
176 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
177 .build();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900178
daniel park796c2eb2018-03-22 17:01:51 +0900179 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Jian Li4d5c5c32018-04-02 16:38:18 +0900180 .punt()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900181 .build();
daniel park796c2eb2018-03-22 17:01:51 +0900182
183 osNodeService.completeNodes(COMPUTE).forEach(node -> {
184 osFlowRuleService.setRule(
185 appId,
186 node.intgBridge(),
187 selector,
188 treatment,
189 PRIORITY_DHCP_RULE,
190 SRC_VNI_TABLE,
191 install);
192 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900193 }
194
195 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 {}",
249 reqInstPort.ipAddress(), clientMac);
250 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 {}",
259 reqInstPort.ipAddress(), clientMac);
260 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) {
286 Port osPort = osNetworkService.port(reqInstPort.portId());
287 // pick one IP address to make a reply
288 IP fixedIp = osPort.getFixedIps().stream().findFirst().get();
289 Subnet osSubnet = osNetworkService.subnet(fixedIp.getSubnetId());
290
291 Ethernet ethReply = new Ethernet();
292 ethReply.setSourceMACAddress(dhcpServerMac);
293 ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
294 ethReply.setEtherType(Ethernet.TYPE_IPV4);
295
296 IPv4 ipv4Request = (IPv4) ethRequest.getPayload();
297 IPv4 ipv4Reply = new IPv4();
298 ipv4Reply.setSourceAddress(Ip4Address.valueOf(osSubnet.getGateway()).toInt());
299 ipv4Reply.setDestinationAddress(reqInstPort.ipAddress().getIp4Address().toInt());
300 ipv4Reply.setTtl(PACKET_TTL);
301
302 UDP udpRequest = (UDP) ipv4Request.getPayload();
303 UDP udpReply = new UDP();
304 udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
305 udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
306
307 DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
308 DHCP dhcpReply = buildDhcpReply(
309 dhcpRequest,
310 packetType,
311 reqInstPort.ipAddress().getIp4Address(),
312 osSubnet);
313
314 udpReply.setPayload(dhcpReply);
315 ipv4Reply.setPayload(udpReply);
316 ethReply.setPayload(ipv4Reply);
317
318 return ethReply;
319 }
320
321 private void sendReply(PacketContext context, Ethernet ethReply) {
322 if (ethReply == null) {
323 return;
324 }
325 ConnectPoint srcPoint = context.inPacket().receivedFrom();
326 TrafficTreatment treatment = DefaultTrafficTreatment
327 .builder()
328 .setOutput(srcPoint.port())
329 .build();
330
331 packetService.emit(new DefaultOutboundPacket(
332 srcPoint.deviceId(),
333 treatment,
334 ByteBuffer.wrap(ethReply.serialize())));
335 context.block();
336 }
337
338 private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
339 Subnet osSubnet) {
340 Ip4Address gatewayIp = Ip4Address.valueOf(osSubnet.getGateway());
341 int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
342
343 DHCP dhcpReply = new DHCP();
344 dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
345 dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
346 dhcpReply.setHardwareAddressLength((byte) 6);
347 dhcpReply.setTransactionId(request.getTransactionId());
348 dhcpReply.setFlags(request.getFlags());
349 dhcpReply.setYourIPAddress(yourIp.toInt());
350 dhcpReply.setServerIPAddress(gatewayIp.toInt());
351 dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress());
352
Yi Tsengc7403c22017-06-19 16:23:22 -0700353 List<DhcpOption> options = Lists.newArrayList();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900354 // message type
Yi Tsengc7403c22017-06-19 16:23:22 -0700355 DhcpOption option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900356 option.setCode(OptionCode_MessageType.getValue());
357 option.setLength((byte) 1);
358 byte[] optionData = {msgType};
359 option.setData(optionData);
360 options.add(option);
361
362 // server identifier
Yi Tsengc7403c22017-06-19 16:23:22 -0700363 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900364 option.setCode(OptionCode_DHCPServerIp.getValue());
365 option.setLength((byte) 4);
366 option.setData(gatewayIp.toOctets());
367 options.add(option);
368
369 // lease time
Yi Tsengc7403c22017-06-19 16:23:22 -0700370 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900371 option.setCode(OptionCode_LeaseTime.getValue());
372 option.setLength((byte) 4);
373 option.setData(DHCP_DATA_LEASE_INFINITE);
374 options.add(option);
375
376 // subnet mask
377 Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700378 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900379 option.setCode(OptionCode_SubnetMask.getValue());
380 option.setLength((byte) 4);
381 option.setData(subnetMask.toOctets());
382 options.add(option);
383
384 // broadcast address
385 Ip4Address broadcast = Ip4Address.makeMaskedAddress(yourIp, subnetPrefixLen);
Yi Tsengc7403c22017-06-19 16:23:22 -0700386 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900387 option.setCode(OptionCode_BroadcastAddress.getValue());
388 option.setLength((byte) 4);
389 option.setData(broadcast.toOctets());
390 options.add(option);
391
392 // domain server
Yi Tsengc7403c22017-06-19 16:23:22 -0700393 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900394 option.setCode(OptionCode_DomainServer.getValue());
395 option.setLength((byte) 4);
396 option.setData(DEFAULT_DNS.toOctets());
397 options.add(option);
398
Yi Tsengc7403c22017-06-19 16:23:22 -0700399 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900400 option.setCode(DHCP_OPTION_MTU);
401 option.setLength((byte) 2);
Jian Lifb005492018-03-02 10:50:15 +0900402 option.setData(ByteBuffer.allocate(2).putShort((short) dhcpDataMtu).array());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900403 options.add(option);
404
405 // router address
Yi Tsengc7403c22017-06-19 16:23:22 -0700406 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900407 option.setCode(OptionCode_RouterAddress.getValue());
408 option.setLength((byte) 4);
409 option.setData(gatewayIp.toOctets());
410 options.add(option);
411
412 // end option
Yi Tsengc7403c22017-06-19 16:23:22 -0700413 option = new DhcpOption();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900414 option.setCode(OptionCode_END.getValue());
415 option.setLength((byte) 1);
416 options.add(option);
417
418 dhcpReply.setOptions(options);
419 return dhcpReply;
420 }
421 }
422}