blob: 011768d9d379acb6dee3faa7f67d19e4d3c02082 [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_POST_METHOD:
458 request = new HttpPost(url);
Jian Li46650932018-09-04 23:52:11 +0900459 HttpEntityEnclosingRequest postRequest =
Jian Li63430202018-08-30 16:24:09 +0900460 (HttpEntityEnclosingRequest) oldRequest;
Jian Li46650932018-09-04 23:52:11 +0900461 ((HttpPost) request).setEntity(postRequest.getEntity());
462 break;
463 case HTTP_PUT_METHOD:
464 request = new HttpPut(url);
465 HttpEntityEnclosingRequest putRequest =
466 (HttpEntityEnclosingRequest) oldRequest;
467 ((HttpPut) request).setEntity(putRequest.getEntity());
468 break;
469 case HTTP_DELETE_METHOD:
470 request = new HttpDelete(url);
Jian Li63430202018-08-30 16:24:09 +0900471 break;
Jian Li6a47fd02018-11-27 21:51:03 +0900472 case HTTP_GET_METHOD:
Jian Li63430202018-08-30 16:24:09 +0900473 default:
474 request = new HttpGet(url);
475 break;
476 }
477
478 // configure headers from original HTTP request
479 for (Header header : oldRequest.getAllHeaders()) {
Jian Li46650932018-09-04 23:52:11 +0900480 if (method.equals(HTTP_POST_METHOD) ||
481 method.equals(HTTP_PUT_METHOD)) {
482 // we DO NOT add duplicated HTTP headers for POST and PUT methods
483 if (excludedHeaders.contains(header.getName().toLowerCase())) {
484 continue;
485 }
486 }
Jian Li63430202018-08-30 16:24:09 +0900487 request.addHeader(header);
488 }
489
490 request.setProtocolVersion(oldRequest.getProtocolVersion());
491
492 Port port = osNetworkService.port(instPort.portId());
493
494 request.addHeader(new BasicHeader(INSTANCE_ID_HEADER, port.getDeviceId()));
Jian Li63430202018-08-30 16:24:09 +0900495 request.addHeader(new BasicHeader(TENANT_ID_HEADER, port.getTenantId()));
Jian Li46650932018-09-04 23:52:11 +0900496 request.addHeader(new BasicHeader(FORWARDED_FOR_HEADER, instPort.ipAddress().toString()));
497 if (metadataSecret() != null) {
498 request.addHeader(new BasicHeader(INSTANCE_ID_SIGNATURE_HEADER,
499 hmacEncrypt(metadataSecret(), port.getDeviceId())));
500 }
Jian Li63430202018-08-30 16:24:09 +0900501
502 try {
503 return client.execute(request);
504 } catch (IOException e) {
505 log.warn("Failed to get response from metadata server due to {}", e);
506 }
507
508 return null;
509 }
510
511 /**
512 * Sends out ethernet frame.
513 *
514 * @param context packet context
515 * @param ethReply ethernet frame
516 */
517 private void sendReply(PacketContext context, Ethernet ethReply) {
518 if (ethReply == null) {
519 return;
520 }
521 ConnectPoint srcPoint = context.inPacket().receivedFrom();
522 TrafficTreatment treatment = DefaultTrafficTreatment
523 .builder()
524 .setOutput(srcPoint.port())
525 .build();
526
527 packetService.emit(new DefaultOutboundPacket(
528 srcPoint.deviceId(),
529 treatment,
530 ByteBuffer.wrap(ethReply.serialize())));
531 context.block();
532 }
Jian Li6a47fd02018-11-27 21:51:03 +0900533
534 private String metadataSecret() {
535 OpenstackNode controller = osNodeService.completeNodes(CONTROLLER)
536 .stream().findFirst().orElse(null);
537
538 if (controller != null && controller.neutronConfig() != null) {
539 return controller.neutronConfig().metadataProxySecret();
540 }
541
542 return null;
543 }
Jian Li63430202018-08-30 16:24:09 +0900544 }
545
546 private class InternalNodeEventListener implements OpenstackNodeListener {
547 @Override
548 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900549 return event.subject().type() == COMPUTE;
550 }
551
552 private boolean isRelevantHelper() {
553 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))
554 && useMetadataProxy();
Jian Li63430202018-08-30 16:24:09 +0900555 }
556
557 @Override
558 public void event(OpenstackNodeEvent event) {
559 OpenstackNode osNode = event.subject();
560 switch (event.type()) {
561 case OPENSTACK_NODE_COMPLETE:
Jian Li6a47fd02018-11-27 21:51:03 +0900562 eventExecutor.execute(() -> processNodeCompletion(osNode));
Jian Li63430202018-08-30 16:24:09 +0900563 break;
564 case OPENSTACK_NODE_INCOMPLETE:
Jian Li6a47fd02018-11-27 21:51:03 +0900565 eventExecutor.execute(() -> processNodeIncompletion(osNode));
Jian Li63430202018-08-30 16:24:09 +0900566 break;
567 case OPENSTACK_NODE_CREATED:
568 case OPENSTACK_NODE_UPDATED:
569 case OPENSTACK_NODE_REMOVED:
570 default:
571 break;
572 }
573 }
574
Jian Li6a47fd02018-11-27 21:51:03 +0900575 private void processNodeCompletion(OpenstackNode osNode) {
576 if (!isRelevantHelper()) {
577 return;
578 }
579
580 setMetadataRule(osNode, true);
581 }
582
583 private void processNodeIncompletion(OpenstackNode osNode) {
584 if (!isRelevantHelper()) {
585 return;
586 }
587
588 setMetadataRule(osNode, false);
589 }
590
Jian Li63430202018-08-30 16:24:09 +0900591 /**
592 * Installs metadata rule for receiving all metadata request packets.
593 *
594 * @param osNode openstack node
595 * @param install installation flag
596 */
597 private void setMetadataRule(OpenstackNode osNode, boolean install) {
598 TrafficSelector selector = DefaultTrafficSelector.builder()
599 .matchEthType(Ethernet.TYPE_IPV4)
600 .matchIPProtocol(IPv4.PROTOCOL_TCP)
601 .matchIPDst(IpPrefix.valueOf(
602 IpAddress.valueOf(METADATA_SERVER_IP), PREFIX_LENGTH))
603 .matchTcpDst(TpPort.tpPort(HTTP_SERVER_PORT))
604 .build();
605
606 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
607 .punt()
608 .build();
609
610 osFlowRuleService.setRule(
611 appId,
612 osNode.intgBridge(),
613 selector,
614 treatment,
615 PRIORITY_DHCP_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900616 DHCP_TABLE,
Jian Li63430202018-08-30 16:24:09 +0900617 install);
618 }
619 }
620
Jian Li46650932018-09-04 23:52:11 +0900621 private boolean useMetadataProxy() {
622 OpenstackNode gw = osNodeService.completeNodes(CONTROLLER)
623 .stream().findFirst().orElse(null);
624
625 if (gw != null && gw.neutronConfig() != null) {
626 return gw.neutronConfig().useMetadataProxy();
627 }
628
629 return false;
630 }
631
Jian Li63430202018-08-30 16:24:09 +0900632 /**
633 * Implements Http packet format.
634 */
635 protected static class Http extends BasePacket {
636
Jian Li411bf2e2018-11-29 00:08:54 +0900637 /**
638 * HTTP packet type.
639 */
Jian Li63430202018-08-30 16:24:09 +0900640 public enum Type {
641
642 /**
643 * Signifies that this is a Http REQUEST packet.
644 */
645 REQUEST,
646
647 /**
648 * Signifies that this is a Http RESPONSE packet.
649 */
650 RESPONSE,
651 }
652
653 private Type type;
654 private HttpMessage message;
655
Jian Li411bf2e2018-11-29 00:08:54 +0900656 Http() {
657 }
658
Jian Li63430202018-08-30 16:24:09 +0900659 /**
660 * Obtains the Http type.
661 *
662 * @return Http type
663 */
664 public Type getType() {
665 return type;
666 }
667
668 /**
669 * Configures the Http type.
670 *
671 * @param type Http type
672 */
673 public void setType(Type type) {
674 this.type = type;
675 }
676
677 /**
678 * Obtains the Http message.
679 *
680 * @return Http message
681 */
682 public HttpMessage getMessage() {
683 return message;
684 }
685
686 /**
687 * Configures the Http message.
688 *
689 * @param message Http message
690 */
691 public void setMessage(HttpMessage message) {
692 this.message = message;
693 }
694
695 @Override
696 public byte[] serialize() {
697 if (type == RESPONSE) {
698
699 byte[] header = unparseHttpResponseHeader((HttpResponse) message);
700 byte[] body = unparseHttpResponseBody((HttpResponse) message);
701
702 if (header == null || body == null) {
703 return new byte[0];
704 }
705
706 final byte[] data = new byte[header.length + body.length];
707 final ByteBuffer bb = ByteBuffer.wrap(data);
708 bb.put(header);
709 bb.put(body);
710
711 return data;
712 }
713 return new byte[0];
714 }
715
716 @Override
717 public boolean equals(Object o) {
718
719 if (this == o) {
720 return true;
721 }
722
723 if (o == null || getClass() != o.getClass()) {
724 return false;
725 }
726
727 if (!super.equals(o)) {
728 return false;
729 }
730
731 Http http = (Http) o;
732 return type == http.type &&
733 com.google.common.base.Objects.equal(message, http.message);
734 }
735
736 @Override
737 public int hashCode() {
738 return Objects.hash(type, message);
739 }
740 }
741}