blob: 995bb21229fc304cdcd543c4d6dda8e994f690ba [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;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070021import org.osgi.service.component.annotations.Activate;
22import org.osgi.service.component.annotations.Component;
23import org.osgi.service.component.annotations.Deactivate;
24import org.osgi.service.component.annotations.Reference;
25import org.osgi.service.component.annotations.ReferenceCardinality;
Jian Li63430202018-08-30 16:24:09 +090026import org.apache.http.Header;
27import org.apache.http.HttpEntityEnclosingRequest;
28import org.apache.http.HttpMessage;
29import org.apache.http.HttpRequest;
30import org.apache.http.HttpResponse;
31import org.apache.http.client.methods.CloseableHttpResponse;
Jian Li46650932018-09-04 23:52:11 +090032import org.apache.http.client.methods.HttpDelete;
Jian Li63430202018-08-30 16:24:09 +090033import org.apache.http.client.methods.HttpGet;
34import org.apache.http.client.methods.HttpPost;
Jian Li46650932018-09-04 23:52:11 +090035import org.apache.http.client.methods.HttpPut;
Jian Li63430202018-08-30 16:24:09 +090036import org.apache.http.client.methods.HttpRequestBase;
37import org.apache.http.impl.client.CloseableHttpClient;
38import org.apache.http.impl.client.HttpClientBuilder;
39import org.apache.http.message.BasicHeader;
40import org.apache.http.message.BasicHttpResponse;
41import org.onlab.packet.BasePacket;
42import org.onlab.packet.Data;
43import org.onlab.packet.Ethernet;
44import org.onlab.packet.IPv4;
45import org.onlab.packet.IpAddress;
46import org.onlab.packet.IpPrefix;
47import org.onlab.packet.TCP;
48import org.onlab.packet.TpPort;
Jian Li63430202018-08-30 16:24:09 +090049import org.onosproject.cluster.ClusterService;
50import org.onosproject.cluster.LeadershipService;
51import org.onosproject.cluster.NodeId;
52import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
54import org.onosproject.net.ConnectPoint;
55import org.onosproject.net.flow.DefaultTrafficSelector;
56import org.onosproject.net.flow.DefaultTrafficTreatment;
57import org.onosproject.net.flow.TrafficSelector;
58import org.onosproject.net.flow.TrafficTreatment;
59import org.onosproject.net.packet.DefaultOutboundPacket;
60import org.onosproject.net.packet.PacketContext;
61import org.onosproject.net.packet.PacketProcessor;
62import org.onosproject.net.packet.PacketService;
63import org.onosproject.openstacknetworking.api.Constants;
64import org.onosproject.openstacknetworking.api.InstancePort;
65import org.onosproject.openstacknetworking.api.InstancePortService;
66import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
67import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
68import org.onosproject.openstacknode.api.OpenstackNode;
69import org.onosproject.openstacknode.api.OpenstackNodeEvent;
70import org.onosproject.openstacknode.api.OpenstackNodeListener;
71import org.onosproject.openstacknode.api.OpenstackNodeService;
72import org.openstack4j.model.network.Port;
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 Li63430202018-08-30 16:24:09 +090079
80import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
81import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
82import static org.onosproject.openstacknetworking.impl.OpenstackMetadataProxyHandler.Http.Type.RESPONSE;
83import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.hmacEncrypt;
84import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.parseHttpRequest;
85import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.unparseHttpResponseBody;
86import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.unparseHttpResponseHeader;
87import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
88import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
89import static org.slf4j.LoggerFactory.getLogger;
90
91/**
92 * Handles metadata requests for the virtual instances.
93 */
94@Component(immediate = true)
95public class OpenstackMetadataProxyHandler {
96 protected final Logger log = getLogger(getClass());
97
98 private static final String METADATA_SERVER_IP = "169.254.169.254";
99 private static final int METADATA_SERVER_PORT = 8775;
100 private static final int HTTP_SERVER_PORT = 80;
101 private static final int PREFIX_LENGTH = 32;
102 private static final short WINDOW_SIZE = (short) 0x1000;
103 private static final short FIN_FLAG = (short) 0x01;
104 private static final short SYN_FLAG = (short) 0x02;
105 private static final short ACK_FLAG = (short) 0x10;
106 private static final short SYN_ACK_FLAG = (short) 0x12;
107 private static final short FIN_ACK_FLAG = (short) 0x11;
108 private static final byte DATA_OFFSET = (byte) 0x5;
109 private static final short URGENT_POINTER = (short) 0x1;
110 private static final byte PACKET_TTL = (byte) 127;
111 private static final String HTTP_PREFIX = "http://";
112 private static final String COLON = ":";
113
114 private static final String INSTANCE_ID_HEADER = "X-Instance-ID";
115 private static final String INSTANCE_ID_SIGNATURE_HEADER = "X-Instance-ID-Signature";
116 private static final String TENANT_ID_HEADER = "X-Tenant-ID";
117 private static final String FORWARDED_FOR_HEADER = "X-Forwarded-For";
118
119 private static final String HTTP_GET_METHOD = "GET";
120 private static final String HTTP_POST_METHOD = "POST";
Jian Li46650932018-09-04 23:52:11 +0900121 private static final String HTTP_PUT_METHOD = "PUT";
122 private static final String HTTP_DELETE_METHOD = "DELETE";
Jian Li63430202018-08-30 16:24:09 +0900123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900125 protected CoreService coreService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900128 protected PacketService packetService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900131 protected ClusterService clusterService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900134 protected LeadershipService leadershipService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900137 protected OpenstackNetworkService osNetworkService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900140 protected OpenstackNodeService osNodeService;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900143 protected InstancePortService instancePortService;
144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li63430202018-08-30 16:24:09 +0900146 protected OpenstackFlowRuleService osFlowRuleService;
147
Jian Li63430202018-08-30 16:24:09 +0900148 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
149 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
150
Jian Li46650932018-09-04 23:52:11 +0900151 private Set<String> excludedHeaders = ImmutableSet.of("content-type", "content-length");
152
Jian Li63430202018-08-30 16:24:09 +0900153 private ApplicationId appId;
154 private NodeId localNodeId;
155
156 @Activate
157 protected void activate() {
158 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
159 localNodeId = clusterService.getLocalNode().id();
Jian Li63430202018-08-30 16:24:09 +0900160 osNodeService.addListener(osNodeListener);
161 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
162 leadershipService.runForLeadership(appId.name());
163
164 log.info("Started");
165 }
166
167 @Deactivate
168 protected void deactivate() {
169 packetService.removeProcessor(packetProcessor);
Jian Li63430202018-08-30 16:24:09 +0900170 osNodeService.removeListener(osNodeListener);
171 leadershipService.withdraw(appId.name());
172
173 log.info("Stopped");
174 }
175
Jian Li63430202018-08-30 16:24:09 +0900176 private class InternalPacketProcessor implements PacketProcessor {
177
178 @Override
179 public void process(PacketContext context) {
Jian Li46650932018-09-04 23:52:11 +0900180
181 if (!useMetadataProxy()) {
182 return;
183 }
184
Jian Li63430202018-08-30 16:24:09 +0900185 if (context.isHandled()) {
186 return;
187 }
188
189 Ethernet ethPacket = context.inPacket().parsed();
190 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) {
191 return;
192 }
193
194 IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
195 if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_TCP ||
196 !IpAddress.valueOf(ipv4Packet.getDestinationAddress()).
197 equals(IpAddress.valueOf(METADATA_SERVER_IP))) {
198 return;
199 }
200
201 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
202 if (tcpPacket.getDestinationPort() != HTTP_SERVER_PORT) {
203 return;
204 }
205
Jian Li46650932018-09-04 23:52:11 +0900206 // (three-way handshaking)
207 // reply TCP SYN-ACK packet with receiving TCP SYN packet
Jian Li63430202018-08-30 16:24:09 +0900208 if (tcpPacket.getFlags() == SYN_FLAG) {
209 Ethernet ethReply = buildTcpSynAckPacket(ethPacket, ipv4Packet, tcpPacket);
210 sendReply(context, ethReply);
211 return;
212 }
213
Jian Li46650932018-09-04 23:52:11 +0900214 // (four-way handshaking)
215 // reply TCP ACK and TCP FIN-ACK packets with receiving TCP FIN-ACK packet
Jian Li63430202018-08-30 16:24:09 +0900216 if (tcpPacket.getFlags() == FIN_ACK_FLAG) {
217 Ethernet ackReply = buildTcpAckPacket(ethPacket, ipv4Packet, tcpPacket);
218 sendReply(context, ackReply);
219 Ethernet finAckReply = buildTcpFinAckPacket(ethPacket, ipv4Packet, tcpPacket);
220 sendReply(context, finAckReply);
221 return;
222 }
223
Jian Li46650932018-09-04 23:52:11 +0900224 // normal TCP data transmission
Jian Li63430202018-08-30 16:24:09 +0900225 Data data = (Data) tcpPacket.getPayload();
226 byte[] byteData = data.getData();
227
228 if (byteData.length != 0) {
229 HttpRequest request = parseHttpRequest(byteData);
230 ConnectPoint cp = context.inPacket().receivedFrom();
231 InstancePort instPort = instancePortService.instancePort(cp.deviceId(), cp.port());
232
233 if (instPort == null || request == null) {
234 log.warn("Cannot send metadata request due to lack of information");
235 return;
236 }
237
238 // attempt to send HTTP request to the meta-data server (nova-api),
Jian Li46650932018-09-04 23:52:11 +0900239 // obtain the HTTP response, relay the response to VM through packet-out
Jian Li63430202018-08-30 16:24:09 +0900240 CloseableHttpResponse proxyResponse = proxyHttpRequest(request, instPort);
241
242 if (proxyResponse == null) {
243 log.warn("No response was received from metadata server");
244 return;
245 }
246
247 HttpResponse response = new BasicHttpResponse(proxyResponse.getStatusLine());
248 response.setEntity(proxyResponse.getEntity());
249 response.setHeaders(proxyResponse.getAllHeaders());
250
251 Http httpResponse = new Http();
252 httpResponse.setType(RESPONSE);
253 httpResponse.setMessage(response);
254
255 TCP tcpReply = buildTcpDataPacket(tcpPacket, byteData.length, response);
256 Ethernet ethReply = buildEthFrame(ethPacket, ipv4Packet, tcpReply);
257 sendReply(context, ethReply);
258
259 try {
260 proxyResponse.close();
261 } catch (IOException e) {
262 log.warn("Failed to close the response connection due to {}", e);
263 }
264 }
265 }
266
267 /**
268 * Builds an ethernet frame contains TCP sync-ack packet generated
269 * from the given TCP sync request packet.
270 *
271 * @param ethRequest ethernet request frame
272 * @param ipv4Request IPv4 request
273 * @param tcpRequest TCP request
274 * @return an ethernet frame contains newly generated TCP reply
275 */
276 private Ethernet buildTcpSynAckPacket(Ethernet ethRequest,
277 IPv4 ipv4Request, TCP tcpRequest) {
278
279 TCP tcpReply = buildTcpSignalPacket(tcpRequest, tcpRequest.getSequence(),
280 tcpRequest.getSequence() + 1, SYN_ACK_FLAG);
281
282 return buildEthFrame(ethRequest, ipv4Request, tcpReply);
283 }
284
285 /**
286 * Builds a TCP ACK packet receiving SYN packet.
287 *
288 * @param ethRequest ethernet request frame
289 * @param ipv4Request IPv4 request
290 * @param tcpRequest TCP request
291 * @return an ethernet frame contains newly generated TCP reply
292 */
293 private Ethernet buildTcpAckPacket(Ethernet ethRequest,
294 IPv4 ipv4Request, TCP tcpRequest) {
295 TCP tcpReply = buildTcpSignalPacket(tcpRequest, tcpRequest.getAcknowledge(),
296 tcpRequest.getSequence() + 1, ACK_FLAG);
297
298 return buildEthFrame(ethRequest, ipv4Request, tcpReply);
299 }
300
301 /**
302 * Builds a TCP FIN-ACK packet receiving FIN-ACK packet.
303 *
304 * @param ethRequest ethernet request frame
305 * @param ipv4Request IPv4 request
306 * @param tcpRequest TCP request
307 * @return an ethernet frame contains newly generated TCP reply
308 */
309 private Ethernet buildTcpFinAckPacket(Ethernet ethRequest,
310 IPv4 ipv4Request, TCP tcpRequest) {
311 TCP tcpReply = buildTcpSignalPacket(tcpRequest, tcpRequest.getAcknowledge(),
312 tcpRequest.getSequence() + 1, FIN_ACK_FLAG);
313
314 return buildEthFrame(ethRequest, ipv4Request, tcpReply);
315 }
316
317 /**
318 * Builds a TCP signaling packet.
319 *
320 * @param tcpRequest TCP request
321 * @param seq sequence number
322 * @param ack ack number
323 * @param flags TCP flags
324 * @return TCP signal packet
325 */
326 private TCP buildTcpSignalPacket(TCP tcpRequest, int seq, int ack, short flags) {
327 TCP tcpReply = new TCP();
328 tcpReply.setSourcePort(tcpRequest.getDestinationPort());
329 tcpReply.setDestinationPort(tcpRequest.getSourcePort());
330 tcpReply.setSequence(seq);
331 tcpReply.setAcknowledge(ack);
332 tcpReply.setDataOffset(DATA_OFFSET);
333 tcpReply.setFlags(flags);
334 tcpReply.setWindowSize(WINDOW_SIZE);
335 tcpReply.setUrgentPointer(URGENT_POINTER);
336
337 return tcpReply;
338 }
339
340 /**
341 * Builds a TCP data packet.
342 *
343 * @param tcpRequest TCP request
344 * @param requestLength TCP request data length
345 * @param response HTTP response
346 * @return a TCP data packet
347 */
348 private TCP buildTcpDataPacket(TCP tcpRequest, int requestLength,
349 HttpResponse response) {
350 TCP tcpReply = new TCP();
351 tcpReply.setSourcePort(tcpRequest.getDestinationPort());
352 tcpReply.setDestinationPort(tcpRequest.getSourcePort());
353 tcpReply.setSequence(tcpRequest.getAcknowledge());
354 tcpReply.setAcknowledge(tcpRequest.getSequence() + requestLength);
355 tcpReply.setDataOffset(DATA_OFFSET); // no options
356 tcpReply.setFlags(ACK_FLAG);
357 tcpReply.setWindowSize(WINDOW_SIZE);
358 tcpReply.setUrgentPointer(URGENT_POINTER);
359
360 Http httpResponse = new Http();
361 httpResponse.setType(RESPONSE);
362 httpResponse.setMessage(response);
363
364 tcpReply.setPayload(httpResponse);
365
366 return tcpReply;
367 }
368
369 /**
370 * Builds an ethernet frame with the given IPv4 and TCP payload.
371 *
372 * @param ethRequest ethernet request frame
373 * @param ipv4Request IPv4 request
374 * @param tcpReply TCP reply
375 * @return an ethernet frame contains TCP payload
376 */
377 private Ethernet buildEthFrame(Ethernet ethRequest, IPv4 ipv4Request,
378 TCP tcpReply) {
379 Ethernet ethReply = new Ethernet();
380 ethReply.setSourceMACAddress(ethRequest.getDestinationMAC());
381 ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
382 ethReply.setEtherType(ethRequest.getEtherType());
383
384 IPv4 ipv4Reply = new IPv4();
385 ipv4Reply.setSourceAddress(ipv4Request.getDestinationAddress());
386 ipv4Reply.setDestinationAddress(ipv4Request.getSourceAddress());
387 ipv4Reply.setTtl(PACKET_TTL);
388
389 ipv4Reply.setPayload(tcpReply);
390 ethReply.setPayload(ipv4Reply);
391
392 return ethReply;
393 }
394
395 /**
Jian Li92b6f292018-09-06 10:57:18 +0900396 * Obtains the metadata path.
397 *
398 * @param uri metadata request URI
399 * @return full metadata path
400 */
401 private String getMetadataPath(String uri) {
402 OpenstackNode controller = osNodeService.completeNodes(CONTROLLER).
403 stream().findFirst().orElse(null);
404 if (controller == null) {
405 return null;
406 }
407
408 String novaMetadataIpTmp = controller.neutronConfig().novaMetadataIp();
409 String novaMetadataIp = novaMetadataIpTmp != null
410 ? novaMetadataIpTmp : controller.managementIp().toString();
411 Integer novaMetadataPortTmp = controller.neutronConfig().novaMetadataPort();
412 int novaMetadataPort = novaMetadataPortTmp != null
413 ? novaMetadataPortTmp : METADATA_SERVER_PORT;
414
415 return HTTP_PREFIX + novaMetadataIp + COLON + novaMetadataPort + uri;
416 }
417
418 /**
Jian Li63430202018-08-30 16:24:09 +0900419 * Proxyies HTTP request.
420 *
421 * @param oldRequest HTTP request
422 * @param instPort instance port
423 * @return HTTP response
424 */
425 private CloseableHttpResponse proxyHttpRequest(HttpRequest oldRequest,
426 InstancePort instPort) {
427
428 CloseableHttpClient client = HttpClientBuilder.create().build();
Jian Li92b6f292018-09-06 10:57:18 +0900429 String url = getMetadataPath(oldRequest.getRequestLine().getUri());
Jian Li63430202018-08-30 16:24:09 +0900430
431 if (StringUtils.isEmpty(url)) {
432 log.warn("The metadata endpoint is not configured!");
433 return null;
434 }
435
Jian Li63430202018-08-30 16:24:09 +0900436 HttpRequestBase request;
437
Jian Li46650932018-09-04 23:52:11 +0900438 String method = oldRequest.getRequestLine().getMethod().toUpperCase();
439
440 log.info("Sending HTTP {} request to metadata endpoint {}...", method, url);
441
442 switch (method) {
Jian Li63430202018-08-30 16:24:09 +0900443 case HTTP_GET_METHOD:
444 request = new HttpGet(url);
445 break;
446 case HTTP_POST_METHOD:
447 request = new HttpPost(url);
Jian Li46650932018-09-04 23:52:11 +0900448 HttpEntityEnclosingRequest postRequest =
Jian Li63430202018-08-30 16:24:09 +0900449 (HttpEntityEnclosingRequest) oldRequest;
Jian Li46650932018-09-04 23:52:11 +0900450 ((HttpPost) request).setEntity(postRequest.getEntity());
451 break;
452 case HTTP_PUT_METHOD:
453 request = new HttpPut(url);
454 HttpEntityEnclosingRequest putRequest =
455 (HttpEntityEnclosingRequest) oldRequest;
456 ((HttpPut) request).setEntity(putRequest.getEntity());
457 break;
458 case HTTP_DELETE_METHOD:
459 request = new HttpDelete(url);
Jian Li63430202018-08-30 16:24:09 +0900460 break;
461 default:
462 request = new HttpGet(url);
463 break;
464 }
465
466 // configure headers from original HTTP request
467 for (Header header : oldRequest.getAllHeaders()) {
Jian Li46650932018-09-04 23:52:11 +0900468 if (method.equals(HTTP_POST_METHOD) ||
469 method.equals(HTTP_PUT_METHOD)) {
470 // we DO NOT add duplicated HTTP headers for POST and PUT methods
471 if (excludedHeaders.contains(header.getName().toLowerCase())) {
472 continue;
473 }
474 }
Jian Li63430202018-08-30 16:24:09 +0900475 request.addHeader(header);
476 }
477
478 request.setProtocolVersion(oldRequest.getProtocolVersion());
479
480 Port port = osNetworkService.port(instPort.portId());
481
482 request.addHeader(new BasicHeader(INSTANCE_ID_HEADER, port.getDeviceId()));
Jian Li63430202018-08-30 16:24:09 +0900483 request.addHeader(new BasicHeader(TENANT_ID_HEADER, port.getTenantId()));
Jian Li46650932018-09-04 23:52:11 +0900484 request.addHeader(new BasicHeader(FORWARDED_FOR_HEADER, instPort.ipAddress().toString()));
485 if (metadataSecret() != null) {
486 request.addHeader(new BasicHeader(INSTANCE_ID_SIGNATURE_HEADER,
487 hmacEncrypt(metadataSecret(), port.getDeviceId())));
488 }
Jian Li63430202018-08-30 16:24:09 +0900489
490 try {
491 return client.execute(request);
492 } catch (IOException e) {
493 log.warn("Failed to get response from metadata server due to {}", e);
494 }
495
496 return null;
497 }
498
499 /**
500 * Sends out ethernet frame.
501 *
502 * @param context packet context
503 * @param ethReply ethernet frame
504 */
505 private void sendReply(PacketContext context, Ethernet ethReply) {
506 if (ethReply == null) {
507 return;
508 }
509 ConnectPoint srcPoint = context.inPacket().receivedFrom();
510 TrafficTreatment treatment = DefaultTrafficTreatment
511 .builder()
512 .setOutput(srcPoint.port())
513 .build();
514
515 packetService.emit(new DefaultOutboundPacket(
516 srcPoint.deviceId(),
517 treatment,
518 ByteBuffer.wrap(ethReply.serialize())));
519 context.block();
520 }
521 }
522
523 private class InternalNodeEventListener implements OpenstackNodeListener {
524 @Override
525 public boolean isRelevant(OpenstackNodeEvent event) {
526 // do not allow to proceed without leadership
527 NodeId leader = leadershipService.getLeader(appId.name());
528 return Objects.equals(localNodeId, leader) &&
Jian Li46650932018-09-04 23:52:11 +0900529 event.subject().type() == COMPUTE && useMetadataProxy();
Jian Li63430202018-08-30 16:24:09 +0900530 }
531
532 @Override
533 public void event(OpenstackNodeEvent event) {
534 OpenstackNode osNode = event.subject();
535 switch (event.type()) {
536 case OPENSTACK_NODE_COMPLETE:
537 setMetadataRule(osNode, true);
538 break;
539 case OPENSTACK_NODE_INCOMPLETE:
540 setMetadataRule(osNode, false);
541 break;
542 case OPENSTACK_NODE_CREATED:
543 case OPENSTACK_NODE_UPDATED:
544 case OPENSTACK_NODE_REMOVED:
545 default:
546 break;
547 }
548 }
549
550 /**
551 * Installs metadata rule for receiving all metadata request packets.
552 *
553 * @param osNode openstack node
554 * @param install installation flag
555 */
556 private void setMetadataRule(OpenstackNode osNode, boolean install) {
557 TrafficSelector selector = DefaultTrafficSelector.builder()
558 .matchEthType(Ethernet.TYPE_IPV4)
559 .matchIPProtocol(IPv4.PROTOCOL_TCP)
560 .matchIPDst(IpPrefix.valueOf(
561 IpAddress.valueOf(METADATA_SERVER_IP), PREFIX_LENGTH))
562 .matchTcpDst(TpPort.tpPort(HTTP_SERVER_PORT))
563 .build();
564
565 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
566 .punt()
567 .build();
568
569 osFlowRuleService.setRule(
570 appId,
571 osNode.intgBridge(),
572 selector,
573 treatment,
574 PRIORITY_DHCP_RULE,
575 DHCP_ARP_TABLE,
576 install);
577 }
578 }
579
Jian Li46650932018-09-04 23:52:11 +0900580 private boolean useMetadataProxy() {
581 OpenstackNode gw = osNodeService.completeNodes(CONTROLLER)
582 .stream().findFirst().orElse(null);
583
584 if (gw != null && gw.neutronConfig() != null) {
585 return gw.neutronConfig().useMetadataProxy();
586 }
587
588 return false;
589 }
590
591 private String metadataSecret() {
592 OpenstackNode controller = osNodeService.completeNodes(CONTROLLER)
593 .stream().findFirst().orElse(null);
594
595 if (controller != null && controller.neutronConfig() != null) {
596 return controller.neutronConfig().metadataProxySecret();
597 }
598
599 return null;
600 }
601
Jian Li63430202018-08-30 16:24:09 +0900602 /**
603 * Implements Http packet format.
604 */
605 protected static class Http extends BasePacket {
606
607 public enum Type {
608
609 /**
610 * Signifies that this is a Http REQUEST packet.
611 */
612 REQUEST,
613
614 /**
615 * Signifies that this is a Http RESPONSE packet.
616 */
617 RESPONSE,
618 }
619
620 private Type type;
621 private HttpMessage message;
622
623 /**
624 * Obtains the Http type.
625 *
626 * @return Http type
627 */
628 public Type getType() {
629 return type;
630 }
631
632 /**
633 * Configures the Http type.
634 *
635 * @param type Http type
636 */
637 public void setType(Type type) {
638 this.type = type;
639 }
640
641 /**
642 * Obtains the Http message.
643 *
644 * @return Http message
645 */
646 public HttpMessage getMessage() {
647 return message;
648 }
649
650 /**
651 * Configures the Http message.
652 *
653 * @param message Http message
654 */
655 public void setMessage(HttpMessage message) {
656 this.message = message;
657 }
658
659 @Override
660 public byte[] serialize() {
661 if (type == RESPONSE) {
662
663 byte[] header = unparseHttpResponseHeader((HttpResponse) message);
664 byte[] body = unparseHttpResponseBody((HttpResponse) message);
665
666 if (header == null || body == null) {
667 return new byte[0];
668 }
669
670 final byte[] data = new byte[header.length + body.length];
671 final ByteBuffer bb = ByteBuffer.wrap(data);
672 bb.put(header);
673 bb.put(body);
674
675 return data;
676 }
677 return new byte[0];
678 }
679
680 @Override
681 public boolean equals(Object o) {
682
683 if (this == o) {
684 return true;
685 }
686
687 if (o == null || getClass() != o.getClass()) {
688 return false;
689 }
690
691 if (!super.equals(o)) {
692 return false;
693 }
694
695 Http http = (Http) o;
696 return type == http.type &&
697 com.google.common.base.Objects.equal(message, http.message);
698 }
699
700 @Override
701 public int hashCode() {
702 return Objects.hash(type, message);
703 }
704 }
705}