blob: 9446976f279917053afea34be3c071a08cadf47e [file] [log] [blame]
Jian Li63430202018-08-30 16:24:09 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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 */
16
17package org.onosproject.openstacknetworking.impl;
18
Jian Li46650932018-09-04 23:52:11 +090019import com.google.common.collect.ImmutableSet;
Jian Li63430202018-08-30 16:24:09 +090020import org.apache.commons.lang.StringUtils;
Jian Li63430202018-08-30 16:24:09 +090021import org.apache.http.Header;
22import org.apache.http.HttpEntityEnclosingRequest;
23import org.apache.http.HttpMessage;
24import org.apache.http.HttpRequest;
25import org.apache.http.HttpResponse;
26import org.apache.http.client.methods.CloseableHttpResponse;
Jian Li46650932018-09-04 23:52:11 +090027import org.apache.http.client.methods.HttpDelete;
Jian Li63430202018-08-30 16:24:09 +090028import org.apache.http.client.methods.HttpGet;
29import org.apache.http.client.methods.HttpPost;
Jian Li46650932018-09-04 23:52:11 +090030import org.apache.http.client.methods.HttpPut;
Jian Li63430202018-08-30 16:24:09 +090031import org.apache.http.client.methods.HttpRequestBase;
32import org.apache.http.impl.client.CloseableHttpClient;
33import org.apache.http.impl.client.HttpClientBuilder;
34import org.apache.http.message.BasicHeader;
35import org.apache.http.message.BasicHttpResponse;
36import org.onlab.packet.BasePacket;
37import org.onlab.packet.Data;
38import org.onlab.packet.Ethernet;
39import org.onlab.packet.IPv4;
40import org.onlab.packet.IpAddress;
41import org.onlab.packet.IpPrefix;
42import org.onlab.packet.TCP;
43import org.onlab.packet.TpPort;
Jian Li63430202018-08-30 16:24:09 +090044import org.onosproject.cluster.ClusterService;
45import org.onosproject.cluster.LeadershipService;
46import org.onosproject.cluster.NodeId;
47import org.onosproject.core.ApplicationId;
48import org.onosproject.core.CoreService;
49import org.onosproject.net.ConnectPoint;
50import org.onosproject.net.flow.DefaultTrafficSelector;
51import org.onosproject.net.flow.DefaultTrafficTreatment;
52import org.onosproject.net.flow.TrafficSelector;
53import org.onosproject.net.flow.TrafficTreatment;
54import org.onosproject.net.packet.DefaultOutboundPacket;
55import org.onosproject.net.packet.PacketContext;
56import org.onosproject.net.packet.PacketProcessor;
57import org.onosproject.net.packet.PacketService;
58import org.onosproject.openstacknetworking.api.Constants;
59import org.onosproject.openstacknetworking.api.InstancePort;
60import org.onosproject.openstacknetworking.api.InstancePortService;
61import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
62import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
63import org.onosproject.openstacknode.api.OpenstackNode;
64import org.onosproject.openstacknode.api.OpenstackNodeEvent;
65import org.onosproject.openstacknode.api.OpenstackNodeListener;
66import org.onosproject.openstacknode.api.OpenstackNodeService;
67import org.openstack4j.model.network.Port;
Jian Li34220ea2018-11-14 01:30:24 +090068import org.osgi.service.component.annotations.Activate;
69import org.osgi.service.component.annotations.Component;
70import org.osgi.service.component.annotations.Deactivate;
71import org.osgi.service.component.annotations.Reference;
72import org.osgi.service.component.annotations.ReferenceCardinality;
Jian Li63430202018-08-30 16:24:09 +090073import org.slf4j.Logger;
74
75import java.io.IOException;
76import java.nio.ByteBuffer;
Jian Li63430202018-08-30 16:24:09 +090077import java.util.Objects;
Jian Li46650932018-09-04 23:52:11 +090078import java.util.Set;
Jian Li34220ea2018-11-14 01:30:24 +090079import java.util.concurrent.ExecutorService;
Jian Li63430202018-08-30 16:24:09 +090080
Jian Li34220ea2018-11-14 01:30:24 +090081import static java.util.concurrent.Executors.newSingleThreadExecutor;
82import static org.onlab.util.Tools.groupedThreads;
Jian Li5c09e212018-10-24 18:23:58 +090083import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
Jian Li63430202018-08-30 16:24:09 +090084import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
85import static org.onosproject.openstacknetworking.impl.OpenstackMetadataProxyHandler.Http.Type.RESPONSE;
86import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.hmacEncrypt;
87import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.parseHttpRequest;
88import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.unparseHttpResponseBody;
89import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.unparseHttpResponseHeader;
90import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
91import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
92import static org.slf4j.LoggerFactory.getLogger;
93
94/**
95 * Handles metadata requests for the virtual instances.
96 */
97@Component(immediate = true)
98public class OpenstackMetadataProxyHandler {
99 protected final Logger log = getLogger(getClass());
100
101 private static final String METADATA_SERVER_IP = "169.254.169.254";
102 private static final int METADATA_SERVER_PORT = 8775;
103 private static final int HTTP_SERVER_PORT = 80;
104 private static final int PREFIX_LENGTH = 32;
105 private static final short WINDOW_SIZE = (short) 0x1000;
106 private static final short FIN_FLAG = (short) 0x01;
107 private static final short SYN_FLAG = (short) 0x02;
108 private static final short ACK_FLAG = (short) 0x10;
109 private static final short SYN_ACK_FLAG = (short) 0x12;
110 private static final short FIN_ACK_FLAG = (short) 0x11;
111 private static final byte DATA_OFFSET = (byte) 0x5;
112 private static final short URGENT_POINTER = (short) 0x1;
113 private static final byte PACKET_TTL = (byte) 127;
114 private static final String HTTP_PREFIX = "http://";
115 private static final String COLON = ":";
116
117 private static final String INSTANCE_ID_HEADER = "X-Instance-ID";
118 private static final String INSTANCE_ID_SIGNATURE_HEADER = "X-Instance-ID-Signature";
119 private static final String TENANT_ID_HEADER = "X-Tenant-ID";
120 private static final String FORWARDED_FOR_HEADER = "X-Forwarded-For";
121
122 private static final String HTTP_GET_METHOD = "GET";
123 private static final String HTTP_POST_METHOD = "POST";
Jian Li46650932018-09-04 23:52:11 +0900124 private static final String HTTP_PUT_METHOD = "PUT";
125 private static final String HTTP_DELETE_METHOD = "DELETE";
Jian Li63430202018-08-30 16:24:09 +0900126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900128 protected CoreService coreService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900131 protected PacketService packetService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900134 protected ClusterService clusterService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900137 protected LeadershipService leadershipService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900140 protected OpenstackNetworkService osNetworkService;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900143 protected OpenstackNodeService osNodeService;
144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900146 protected InstancePortService instancePortService;
147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900149 protected OpenstackFlowRuleService osFlowRuleService;
150
Jian Li34220ea2018-11-14 01:30:24 +0900151 private final ExecutorService eventExecutor = newSingleThreadExecutor(
152 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Jian Li63430202018-08-30 16:24:09 +0900153 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
154 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
155
Jian Li46650932018-09-04 23:52:11 +0900156 private Set<String> excludedHeaders = ImmutableSet.of("content-type", "content-length");
157
Jian Li63430202018-08-30 16:24:09 +0900158 private ApplicationId appId;
159 private NodeId localNodeId;
160
161 @Activate
162 protected void activate() {
163 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
164 localNodeId = clusterService.getLocalNode().id();
Jian Li63430202018-08-30 16:24:09 +0900165 osNodeService.addListener(osNodeListener);
166 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
167 leadershipService.runForLeadership(appId.name());
168
169 log.info("Started");
170 }
171
172 @Deactivate
173 protected void deactivate() {
174 packetService.removeProcessor(packetProcessor);
Jian Li63430202018-08-30 16:24:09 +0900175 osNodeService.removeListener(osNodeListener);
176 leadershipService.withdraw(appId.name());
Jian Li34220ea2018-11-14 01:30:24 +0900177 eventExecutor.shutdown();
Jian Li63430202018-08-30 16:24:09 +0900178
179 log.info("Stopped");
180 }
181
Jian Li63430202018-08-30 16:24:09 +0900182 private class InternalPacketProcessor implements PacketProcessor {
183
184 @Override
185 public void process(PacketContext context) {
Jian Li46650932018-09-04 23:52:11 +0900186
Jian Li34220ea2018-11-14 01:30:24 +0900187 if (context.isHandled()) {
Jian Li46650932018-09-04 23:52:11 +0900188 return;
189 }
190
Jian Li34220ea2018-11-14 01:30:24 +0900191 // FIXME: need to find a way to spawn a new thread to check metadata proxy mode
192 if (!useMetadataProxy()) {
Jian Li63430202018-08-30 16:24:09 +0900193 return;
194 }
195
196 Ethernet ethPacket = context.inPacket().parsed();
197 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) {
198 return;
199 }
200
201 IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
202 if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_TCP ||
203 !IpAddress.valueOf(ipv4Packet.getDestinationAddress()).
204 equals(IpAddress.valueOf(METADATA_SERVER_IP))) {
205 return;
206 }
207
208 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
209 if (tcpPacket.getDestinationPort() != HTTP_SERVER_PORT) {
210 return;
211 }
212
Jian Li46650932018-09-04 23:52:11 +0900213 // (three-way handshaking)
214 // reply TCP SYN-ACK packet with receiving TCP SYN packet
Jian Li63430202018-08-30 16:24:09 +0900215 if (tcpPacket.getFlags() == SYN_FLAG) {
216 Ethernet ethReply = buildTcpSynAckPacket(ethPacket, ipv4Packet, tcpPacket);
217 sendReply(context, ethReply);
218 return;
219 }
220
Jian Li46650932018-09-04 23:52:11 +0900221 // (four-way handshaking)
222 // reply TCP ACK and TCP FIN-ACK packets with receiving TCP FIN-ACK packet
Jian Li63430202018-08-30 16:24:09 +0900223 if (tcpPacket.getFlags() == FIN_ACK_FLAG) {
224 Ethernet ackReply = buildTcpAckPacket(ethPacket, ipv4Packet, tcpPacket);
225 sendReply(context, ackReply);
226 Ethernet finAckReply = buildTcpFinAckPacket(ethPacket, ipv4Packet, tcpPacket);
227 sendReply(context, finAckReply);
228 return;
229 }
230
Jian Li46650932018-09-04 23:52:11 +0900231 // normal TCP data transmission
Jian Li63430202018-08-30 16:24:09 +0900232 Data data = (Data) tcpPacket.getPayload();
233 byte[] byteData = data.getData();
234
235 if (byteData.length != 0) {
Jian Li34220ea2018-11-14 01:30:24 +0900236 eventExecutor.execute(() -> {
237 processHttpRequest(context, ethPacket, ipv4Packet, tcpPacket, byteData);
238 });
239 }
240 }
Jian Li63430202018-08-30 16:24:09 +0900241
Jian Li34220ea2018-11-14 01:30:24 +0900242 private void processHttpRequest(PacketContext context, Ethernet ethPacket,
243 IPv4 ipv4Packet, TCP tcpPacket, byte[] byteData) {
244 HttpRequest request = parseHttpRequest(byteData);
245 ConnectPoint cp = context.inPacket().receivedFrom();
246 InstancePort instPort = instancePortService.instancePort(cp.deviceId(), cp.port());
Jian Li63430202018-08-30 16:24:09 +0900247
Jian Li34220ea2018-11-14 01:30:24 +0900248 if (instPort == null || request == null) {
249 log.warn("Cannot send metadata request due to lack of information");
250 return;
251 }
Jian Li63430202018-08-30 16:24:09 +0900252
Jian Li34220ea2018-11-14 01:30:24 +0900253 // attempt to send HTTP request to the meta-data server (nova-api),
254 // obtain the HTTP response, relay the response to VM through packet-out
255 CloseableHttpResponse proxyResponse = proxyHttpRequest(request, instPort);
Jian Li63430202018-08-30 16:24:09 +0900256
Jian Li34220ea2018-11-14 01:30:24 +0900257 if (proxyResponse == null) {
258 log.warn("No response was received from metadata server");
259 return;
260 }
Jian Li63430202018-08-30 16:24:09 +0900261
Jian Li34220ea2018-11-14 01:30:24 +0900262 HttpResponse response = new BasicHttpResponse(proxyResponse.getStatusLine());
263 response.setEntity(proxyResponse.getEntity());
264 response.setHeaders(proxyResponse.getAllHeaders());
Jian Li63430202018-08-30 16:24:09 +0900265
Jian Li34220ea2018-11-14 01:30:24 +0900266 Http httpResponse = new Http();
267 httpResponse.setType(RESPONSE);
268 httpResponse.setMessage(response);
Jian Li63430202018-08-30 16:24:09 +0900269
Jian Li34220ea2018-11-14 01:30:24 +0900270 TCP tcpReply = buildTcpDataPacket(tcpPacket, byteData.length, response);
271 Ethernet ethReply = buildEthFrame(ethPacket, ipv4Packet, tcpReply);
272 sendReply(context, ethReply);
273
274 try {
275 proxyResponse.close();
276 } catch (IOException e) {
277 log.warn("Failed to close the response connection due to {}", e);
Jian Li63430202018-08-30 16:24:09 +0900278 }
279 }
280
281 /**
282 * Builds an ethernet frame contains TCP sync-ack packet generated
283 * from the given TCP sync request packet.
284 *
285 * @param ethRequest ethernet request frame
286 * @param ipv4Request IPv4 request
287 * @param tcpRequest TCP request
288 * @return an ethernet frame contains newly generated TCP reply
289 */
290 private Ethernet buildTcpSynAckPacket(Ethernet ethRequest,
291 IPv4 ipv4Request, TCP tcpRequest) {
292
293 TCP tcpReply = buildTcpSignalPacket(tcpRequest, tcpRequest.getSequence(),
294 tcpRequest.getSequence() + 1, SYN_ACK_FLAG);
295
296 return buildEthFrame(ethRequest, ipv4Request, tcpReply);
297 }
298
299 /**
300 * Builds a TCP ACK packet receiving SYN packet.
301 *
302 * @param ethRequest ethernet request frame
303 * @param ipv4Request IPv4 request
304 * @param tcpRequest TCP request
305 * @return an ethernet frame contains newly generated TCP reply
306 */
307 private Ethernet buildTcpAckPacket(Ethernet ethRequest,
308 IPv4 ipv4Request, TCP tcpRequest) {
309 TCP tcpReply = buildTcpSignalPacket(tcpRequest, tcpRequest.getAcknowledge(),
310 tcpRequest.getSequence() + 1, ACK_FLAG);
311
312 return buildEthFrame(ethRequest, ipv4Request, tcpReply);
313 }
314
315 /**
316 * Builds a TCP FIN-ACK packet receiving FIN-ACK packet.
317 *
318 * @param ethRequest ethernet request frame
319 * @param ipv4Request IPv4 request
320 * @param tcpRequest TCP request
321 * @return an ethernet frame contains newly generated TCP reply
322 */
323 private Ethernet buildTcpFinAckPacket(Ethernet ethRequest,
324 IPv4 ipv4Request, TCP tcpRequest) {
325 TCP tcpReply = buildTcpSignalPacket(tcpRequest, tcpRequest.getAcknowledge(),
326 tcpRequest.getSequence() + 1, FIN_ACK_FLAG);
327
328 return buildEthFrame(ethRequest, ipv4Request, tcpReply);
329 }
330
331 /**
332 * Builds a TCP signaling packet.
333 *
334 * @param tcpRequest TCP request
335 * @param seq sequence number
336 * @param ack ack number
337 * @param flags TCP flags
338 * @return TCP signal packet
339 */
340 private TCP buildTcpSignalPacket(TCP tcpRequest, int seq, int ack, short flags) {
341 TCP tcpReply = new TCP();
342 tcpReply.setSourcePort(tcpRequest.getDestinationPort());
343 tcpReply.setDestinationPort(tcpRequest.getSourcePort());
344 tcpReply.setSequence(seq);
345 tcpReply.setAcknowledge(ack);
346 tcpReply.setDataOffset(DATA_OFFSET);
347 tcpReply.setFlags(flags);
348 tcpReply.setWindowSize(WINDOW_SIZE);
349 tcpReply.setUrgentPointer(URGENT_POINTER);
350
351 return tcpReply;
352 }
353
354 /**
355 * Builds a TCP data packet.
356 *
357 * @param tcpRequest TCP request
358 * @param requestLength TCP request data length
359 * @param response HTTP response
360 * @return a TCP data packet
361 */
362 private TCP buildTcpDataPacket(TCP tcpRequest, int requestLength,
363 HttpResponse response) {
364 TCP tcpReply = new TCP();
365 tcpReply.setSourcePort(tcpRequest.getDestinationPort());
366 tcpReply.setDestinationPort(tcpRequest.getSourcePort());
367 tcpReply.setSequence(tcpRequest.getAcknowledge());
368 tcpReply.setAcknowledge(tcpRequest.getSequence() + requestLength);
369 tcpReply.setDataOffset(DATA_OFFSET); // no options
370 tcpReply.setFlags(ACK_FLAG);
371 tcpReply.setWindowSize(WINDOW_SIZE);
372 tcpReply.setUrgentPointer(URGENT_POINTER);
373
374 Http httpResponse = new Http();
375 httpResponse.setType(RESPONSE);
376 httpResponse.setMessage(response);
377
378 tcpReply.setPayload(httpResponse);
379
380 return tcpReply;
381 }
382
383 /**
384 * Builds an ethernet frame with the given IPv4 and TCP payload.
385 *
386 * @param ethRequest ethernet request frame
387 * @param ipv4Request IPv4 request
388 * @param tcpReply TCP reply
389 * @return an ethernet frame contains TCP payload
390 */
391 private Ethernet buildEthFrame(Ethernet ethRequest, IPv4 ipv4Request,
392 TCP tcpReply) {
393 Ethernet ethReply = new Ethernet();
394 ethReply.setSourceMACAddress(ethRequest.getDestinationMAC());
395 ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
396 ethReply.setEtherType(ethRequest.getEtherType());
397
398 IPv4 ipv4Reply = new IPv4();
399 ipv4Reply.setSourceAddress(ipv4Request.getDestinationAddress());
400 ipv4Reply.setDestinationAddress(ipv4Request.getSourceAddress());
401 ipv4Reply.setTtl(PACKET_TTL);
402
403 ipv4Reply.setPayload(tcpReply);
404 ethReply.setPayload(ipv4Reply);
405
406 return ethReply;
407 }
408
409 /**
Jian Li92b6f292018-09-06 10:57:18 +0900410 * Obtains the metadata path.
411 *
412 * @param uri metadata request URI
413 * @return full metadata path
414 */
415 private String getMetadataPath(String uri) {
416 OpenstackNode controller = osNodeService.completeNodes(CONTROLLER).
417 stream().findFirst().orElse(null);
418 if (controller == null) {
419 return null;
420 }
421
422 String novaMetadataIpTmp = controller.neutronConfig().novaMetadataIp();
423 String novaMetadataIp = novaMetadataIpTmp != null
424 ? novaMetadataIpTmp : controller.managementIp().toString();
425 Integer novaMetadataPortTmp = controller.neutronConfig().novaMetadataPort();
426 int novaMetadataPort = novaMetadataPortTmp != null
427 ? novaMetadataPortTmp : METADATA_SERVER_PORT;
428
429 return HTTP_PREFIX + novaMetadataIp + COLON + novaMetadataPort + uri;
430 }
431
432 /**
Jian Li63430202018-08-30 16:24:09 +0900433 * Proxyies HTTP request.
434 *
435 * @param oldRequest HTTP request
436 * @param instPort instance port
437 * @return HTTP response
438 */
439 private CloseableHttpResponse proxyHttpRequest(HttpRequest oldRequest,
440 InstancePort instPort) {
441
442 CloseableHttpClient client = HttpClientBuilder.create().build();
Jian Li92b6f292018-09-06 10:57:18 +0900443 String url = getMetadataPath(oldRequest.getRequestLine().getUri());
Jian Li63430202018-08-30 16:24:09 +0900444
445 if (StringUtils.isEmpty(url)) {
446 log.warn("The metadata endpoint is not configured!");
447 return null;
448 }
449
Jian Li63430202018-08-30 16:24:09 +0900450 HttpRequestBase request;
451
Jian Li46650932018-09-04 23:52:11 +0900452 String method = oldRequest.getRequestLine().getMethod().toUpperCase();
453
454 log.info("Sending HTTP {} request to metadata endpoint {}...", method, url);
455
456 switch (method) {
Jian Li63430202018-08-30 16:24:09 +0900457 case HTTP_GET_METHOD:
458 request = new HttpGet(url);
459 break;
460 case HTTP_POST_METHOD:
461 request = new HttpPost(url);
Jian Li46650932018-09-04 23:52:11 +0900462 HttpEntityEnclosingRequest postRequest =
Jian Li63430202018-08-30 16:24:09 +0900463 (HttpEntityEnclosingRequest) oldRequest;
Jian Li46650932018-09-04 23:52:11 +0900464 ((HttpPost) request).setEntity(postRequest.getEntity());
465 break;
466 case HTTP_PUT_METHOD:
467 request = new HttpPut(url);
468 HttpEntityEnclosingRequest putRequest =
469 (HttpEntityEnclosingRequest) oldRequest;
470 ((HttpPut) request).setEntity(putRequest.getEntity());
471 break;
472 case HTTP_DELETE_METHOD:
473 request = new HttpDelete(url);
Jian Li63430202018-08-30 16:24:09 +0900474 break;
475 default:
476 request = new HttpGet(url);
477 break;
478 }
479
480 // configure headers from original HTTP request
481 for (Header header : oldRequest.getAllHeaders()) {
Jian Li46650932018-09-04 23:52:11 +0900482 if (method.equals(HTTP_POST_METHOD) ||
483 method.equals(HTTP_PUT_METHOD)) {
484 // we DO NOT add duplicated HTTP headers for POST and PUT methods
485 if (excludedHeaders.contains(header.getName().toLowerCase())) {
486 continue;
487 }
488 }
Jian Li63430202018-08-30 16:24:09 +0900489 request.addHeader(header);
490 }
491
492 request.setProtocolVersion(oldRequest.getProtocolVersion());
493
494 Port port = osNetworkService.port(instPort.portId());
495
496 request.addHeader(new BasicHeader(INSTANCE_ID_HEADER, port.getDeviceId()));
Jian Li63430202018-08-30 16:24:09 +0900497 request.addHeader(new BasicHeader(TENANT_ID_HEADER, port.getTenantId()));
Jian Li46650932018-09-04 23:52:11 +0900498 request.addHeader(new BasicHeader(FORWARDED_FOR_HEADER, instPort.ipAddress().toString()));
499 if (metadataSecret() != null) {
500 request.addHeader(new BasicHeader(INSTANCE_ID_SIGNATURE_HEADER,
501 hmacEncrypt(metadataSecret(), port.getDeviceId())));
502 }
Jian Li63430202018-08-30 16:24:09 +0900503
504 try {
505 return client.execute(request);
506 } catch (IOException e) {
507 log.warn("Failed to get response from metadata server due to {}", e);
508 }
509
510 return null;
511 }
512
513 /**
514 * Sends out ethernet frame.
515 *
516 * @param context packet context
517 * @param ethReply ethernet frame
518 */
519 private void sendReply(PacketContext context, Ethernet ethReply) {
520 if (ethReply == null) {
521 return;
522 }
523 ConnectPoint srcPoint = context.inPacket().receivedFrom();
524 TrafficTreatment treatment = DefaultTrafficTreatment
525 .builder()
526 .setOutput(srcPoint.port())
527 .build();
528
529 packetService.emit(new DefaultOutboundPacket(
530 srcPoint.deviceId(),
531 treatment,
532 ByteBuffer.wrap(ethReply.serialize())));
533 context.block();
534 }
535 }
536
537 private class InternalNodeEventListener implements OpenstackNodeListener {
538 @Override
539 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900540 return event.subject().type() == COMPUTE;
541 }
542
543 private boolean isRelevantHelper() {
544 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))
545 && useMetadataProxy();
Jian Li63430202018-08-30 16:24:09 +0900546 }
547
548 @Override
549 public void event(OpenstackNodeEvent event) {
550 OpenstackNode osNode = event.subject();
551 switch (event.type()) {
552 case OPENSTACK_NODE_COMPLETE:
Jian Li34220ea2018-11-14 01:30:24 +0900553
554 eventExecutor.execute(() -> {
555
556 if (!isRelevantHelper()) {
557 return;
558 }
559
560 setMetadataRule(osNode, true);
561 });
562
Jian Li63430202018-08-30 16:24:09 +0900563 break;
564 case OPENSTACK_NODE_INCOMPLETE:
Jian Li34220ea2018-11-14 01:30:24 +0900565
566 eventExecutor.execute(() -> {
567
568 if (!isRelevantHelper()) {
569 return;
570 }
571
572 setMetadataRule(osNode, false);
573 });
574
Jian Li63430202018-08-30 16:24:09 +0900575 break;
576 case OPENSTACK_NODE_CREATED:
577 case OPENSTACK_NODE_UPDATED:
578 case OPENSTACK_NODE_REMOVED:
579 default:
580 break;
581 }
582 }
583
584 /**
585 * Installs metadata rule for receiving all metadata request packets.
586 *
587 * @param osNode openstack node
588 * @param install installation flag
589 */
590 private void setMetadataRule(OpenstackNode osNode, boolean install) {
591 TrafficSelector selector = DefaultTrafficSelector.builder()
592 .matchEthType(Ethernet.TYPE_IPV4)
593 .matchIPProtocol(IPv4.PROTOCOL_TCP)
594 .matchIPDst(IpPrefix.valueOf(
595 IpAddress.valueOf(METADATA_SERVER_IP), PREFIX_LENGTH))
596 .matchTcpDst(TpPort.tpPort(HTTP_SERVER_PORT))
597 .build();
598
599 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
600 .punt()
601 .build();
602
603 osFlowRuleService.setRule(
604 appId,
605 osNode.intgBridge(),
606 selector,
607 treatment,
608 PRIORITY_DHCP_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900609 DHCP_TABLE,
Jian Li63430202018-08-30 16:24:09 +0900610 install);
611 }
612 }
613
Jian Li46650932018-09-04 23:52:11 +0900614 private boolean useMetadataProxy() {
615 OpenstackNode gw = osNodeService.completeNodes(CONTROLLER)
616 .stream().findFirst().orElse(null);
617
618 if (gw != null && gw.neutronConfig() != null) {
619 return gw.neutronConfig().useMetadataProxy();
620 }
621
622 return false;
623 }
624
625 private String metadataSecret() {
626 OpenstackNode controller = osNodeService.completeNodes(CONTROLLER)
627 .stream().findFirst().orElse(null);
628
629 if (controller != null && controller.neutronConfig() != null) {
630 return controller.neutronConfig().metadataProxySecret();
631 }
632
633 return null;
634 }
635
Jian Li63430202018-08-30 16:24:09 +0900636 /**
637 * Implements Http packet format.
638 */
639 protected static class Http extends BasePacket {
640
641 public enum Type {
642
643 /**
644 * Signifies that this is a Http REQUEST packet.
645 */
646 REQUEST,
647
648 /**
649 * Signifies that this is a Http RESPONSE packet.
650 */
651 RESPONSE,
652 }
653
654 private Type type;
655 private HttpMessage message;
656
657 /**
658 * Obtains the Http type.
659 *
660 * @return Http type
661 */
662 public Type getType() {
663 return type;
664 }
665
666 /**
667 * Configures the Http type.
668 *
669 * @param type Http type
670 */
671 public void setType(Type type) {
672 this.type = type;
673 }
674
675 /**
676 * Obtains the Http message.
677 *
678 * @return Http message
679 */
680 public HttpMessage getMessage() {
681 return message;
682 }
683
684 /**
685 * Configures the Http message.
686 *
687 * @param message Http message
688 */
689 public void setMessage(HttpMessage message) {
690 this.message = message;
691 }
692
693 @Override
694 public byte[] serialize() {
695 if (type == RESPONSE) {
696
697 byte[] header = unparseHttpResponseHeader((HttpResponse) message);
698 byte[] body = unparseHttpResponseBody((HttpResponse) message);
699
700 if (header == null || body == null) {
701 return new byte[0];
702 }
703
704 final byte[] data = new byte[header.length + body.length];
705 final ByteBuffer bb = ByteBuffer.wrap(data);
706 bb.put(header);
707 bb.put(body);
708
709 return data;
710 }
711 return new byte[0];
712 }
713
714 @Override
715 public boolean equals(Object o) {
716
717 if (this == o) {
718 return true;
719 }
720
721 if (o == null || getClass() != o.getClass()) {
722 return false;
723 }
724
725 if (!super.equals(o)) {
726 return false;
727 }
728
729 Http http = (Http) o;
730 return type == http.type &&
731 com.google.common.base.Objects.equal(message, http.message);
732 }
733
734 @Override
735 public int hashCode() {
736 return Objects.hash(type, message);
737 }
738 }
739}