blob: a26ae309c8107bd3c0cce288479e4c8cd5cb8b37 [file] [log] [blame]
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -07001/*
2 * Copyright 2017-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 */
16package org.onosproject.artemis.impl;
17
18import com.fasterxml.jackson.core.JsonProcessingException;
19import com.fasterxml.jackson.databind.ObjectMapper;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
22import io.netty.buffer.ByteBuf;
23import io.netty.buffer.Unpooled;
24import io.netty.channel.ChannelHandlerContext;
25import io.netty.util.CharsetUtil;
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070026import org.json.JSONObject;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
29import org.onlab.packet.TpPort;
30import org.onosproject.artemis.ArtemisDeaggregator;
31import org.onosproject.artemis.ArtemisEventListener;
32import org.onosproject.artemis.ArtemisMoasAgent;
33import org.onosproject.artemis.ArtemisPacketProcessor;
34import org.onosproject.artemis.ArtemisService;
35import org.onosproject.artemis.BgpSpeakers;
36import org.onosproject.artemis.impl.bgpspeakers.QuaggaBgpSpeakers;
37import org.onosproject.artemis.impl.moas.MoasClientController;
38import org.onosproject.artemis.impl.moas.MoasServerController;
39import org.onosproject.artemis.impl.objects.ArtemisMessage;
40import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.Port;
44import org.onosproject.net.PortNumber;
45import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
47import org.onosproject.net.device.DeviceService;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.FlowRuleService;
51import org.onosproject.net.flow.TrafficSelector;
52import org.onosproject.net.flow.TrafficTreatment;
53import org.onosproject.net.flowobjective.DefaultForwardingObjective;
54import org.onosproject.net.flowobjective.FlowObjectiveService;
55import org.onosproject.net.flowobjective.ForwardingObjective;
56import org.onosproject.net.intf.InterfaceService;
57import org.onosproject.ovsdb.controller.OvsdbBridge;
58import org.onosproject.ovsdb.controller.OvsdbClientService;
59import org.onosproject.ovsdb.controller.OvsdbController;
60import org.onosproject.ovsdb.controller.OvsdbInterface;
61import org.onosproject.routing.bgp.BgpInfoService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062import org.osgi.service.component.annotations.Activate;
63import org.osgi.service.component.annotations.Component;
64import org.osgi.service.component.annotations.Deactivate;
65import org.osgi.service.component.annotations.Reference;
66import org.osgi.service.component.annotations.ReferenceCardinality;
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070067import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
69
70import java.util.Map;
71import java.util.Optional;
72import java.util.Set;
73
74import static org.onlab.packet.Ethernet.TYPE_IPV4;
75
Ray Milkeyd84f89b2018-08-17 14:54:17 -070076@Component(immediate = true, service = ArtemisDeaggregator.class)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070077public class ArtemisDeaggregatorImpl implements ArtemisDeaggregator {
78
79 private final Logger log = LoggerFactory.getLogger(getClass());
80 private static final int PRIORITY = 1000;
81
82 /* Services */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070083 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070084 private BgpInfoService bgpInfoService;
85
Ray Milkeyd84f89b2018-08-17 14:54:17 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070087 private ArtemisService artemisService;
88
Ray Milkeyd84f89b2018-08-17 14:54:17 -070089 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070090 private OvsdbController ovsdbController;
91
Ray Milkeyd84f89b2018-08-17 14:54:17 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070093 private DeviceService deviceService;
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070096 private InterfaceService interfaceService;
97
Ray Milkeyd84f89b2018-08-17 14:54:17 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -070099 private FlowObjectiveService flowObjectiveService;
100
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700102 private FlowRuleService flowRuleService;
103
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700105 private CoreService coreService;
106
107 /* Variables */
108 private Set<BgpSpeakers> bgpSpeakers = Sets.newHashSet();
109 private MoasServerController moasServer;
110
111 private Port tunnelPort = null;
112 private ApplicationId appId;
113
114 private IpAddress remoteTunnelIp = null;
115 private IpPrefix remotePrefix = null;
116 private boolean rulesInstalled;
117
118 /* Agent */
119 private InternalMoasAgent moasAgent = new InternalMoasAgent();
120 private InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
121 private InternalDeviceListener deviceListener = new InternalDeviceListener();
122
123 private Set<MoasClientController> moasClientControllers = Sets.newConcurrentHashSet();
124
125 private final ArtemisEventListener artemisEventListener = this::handleArtemisEvent;
126
127 @Activate
128 protected void activate() {
129 rulesInstalled = false;
130
131 // FIXME: add other type of BGP Speakers when Dynamic Configuration is available
132 bgpSpeakers.add(new QuaggaBgpSpeakers(bgpInfoService));
133
134 moasServer = new MoasServerController();
135 moasServer.start(moasAgent, packetProcessor);
136
137 deviceService.addListener(deviceListener);
138
139 appId = coreService.getAppId("org.onosproject.artemis");
140
141 // enable OVSDB for the switches that we will install the GRE tunnel
142 artemisService.getConfig().ifPresent(config -> config.moasInfo().getTunnelPoints()
143 .forEach(tunnelPoint -> ovsdbController.connect(tunnelPoint.getOvsdbIp(), TpPort.tpPort(6640)))
144 );
145
146 artemisService.addListener(artemisEventListener);
147
148 log.info("Artemis Deaggregator Service Started");
149
150 /*
151 log.info("interfaces {}", interfaceService.getInterfaces());
152
153 [{
154 "name": "",
155 "connectPoint": "of:000000000000000a/2",
156 "ipAddresses": "[1.1.1.1/30]",
157 "macAddress": "00:00:00:00:00:01"
158 },
159 {
160 "name": "",
161 "connectPoint": "of:000000000000000a/3",
162 "ipAddresses": "[10.0.0.1/8]",
163 "macAddress": "00:00:00:00:00:01"
164 }]
165 */
166 }
167
168 @Deactivate
169 protected void deactivate() {
170 moasServer.stop();
171
172 moasClientControllers.forEach(MoasClientController::stop);
173 moasClientControllers.clear();
174
175 flowRuleService.removeFlowRulesById(appId);
176 deviceService.removeListener(deviceListener);
177
178 remoteTunnelIp = null;
179 remotePrefix = null;
180 tunnelPort = null;
181
182 artemisService.removeListener(artemisEventListener);
183
184 log.info("Artemis Deaggregator Service Stopped");
185 }
186
187 /**
188 * Create a GRE tunnel interface pointing to remote MOAS.
189 *
190 * @param remoteIp remote ip on GRE tunnel
191 */
192 private void createTunnelInterface(IpAddress remoteIp) {
193 ovsdbController.getNodeIds().forEach(nodeId -> artemisService.getConfig().flatMap(config ->
194 config.moasInfo().getTunnelPoints()
195 .stream()
196 .filter(tunnelPoint -> tunnelPoint.getOvsdbIp().toString().equals(nodeId.getIpAddress()))
197 .findFirst()
198 ).ifPresent(tunnelPoint -> {
199 OvsdbClientService ovsdbClient = ovsdbController.getOvsdbClient(nodeId);
200 ovsdbClient.dropInterface("gre-int");
201 Map<String, String> options = Maps.newHashMap();
202 options.put("remote_ip", remoteIp.toString());
203 OvsdbInterface ovsdbInterface = OvsdbInterface.builder()
204 .name("gre-int")
205 .options(options)
206 .type(OvsdbInterface.Type.GRE)
207 .build();
208 OvsdbBridge mainBridge = ovsdbClient.getBridges().iterator().next();
209 ovsdbClient.createInterface(mainBridge.name(), ovsdbInterface);
210 log.info("Tunnel setup at {} - {}", nodeId, tunnelPoint);
211 }));
212 }
213
214 /**
215 * Install rules.
216 */
217 private void installRules() {
218 log.info("Remote Data {} - {} - {}", tunnelPort, remoteTunnelIp, remotePrefix);
219 // FIXME: currently works only for a simple pair of client-server
220 if (!rulesInstalled && tunnelPort != null && remoteTunnelIp != null) {
221 if (remotePrefix != null) {
222 installServerRules();
223 } else {
224 installClientRules();
225 }
226 rulesInstalled = true;
227 }
228 }
229
230 /**
231 * Rules to be installed on MOAS Client.
232 */
233 private void installClientRules() {
234 log.info("installClientRules");
235 artemisService.getConfig().ifPresent(config -> {
236 // selector
237 TrafficSelector selector = DefaultTrafficSelector.builder()
238 .matchEthType(TYPE_IPV4)
239 .matchIPSrc(remoteTunnelIp.toIpPrefix())
240 .matchIPDst(config.moasInfo().getTunnelPoint().getLocalIp().toIpPrefix())
241 .build();
242 // treatment
243 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
244 .setOutput(PortNumber.LOCAL)
245 .build();
246 // forwarding objective builder
247 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
248 .withSelector(selector)
249 .withTreatment(treatment)
250 .withPriority(PRIORITY)
251 .withFlag(ForwardingObjective.Flag.VERSATILE)
252 .fromApp(appId)
253 .add();
254 // send flow objective to specified switch
255 flowObjectiveService.forward(DeviceId.deviceId(tunnelPort.element().id().toString()),
256 forwardingObjective);
257
258 log.info("Installing flow rule = {}", forwardingObjective);
259 });
260 }
261
262 /**
263 * Rules to be isntalled on MOAS Server.
264 */
265 private void installServerRules() {
266 log.info("installServerRules");
267 artemisService.getConfig().ifPresent(config -> {
268 // selector
269 TrafficSelector selector = DefaultTrafficSelector.builder()
270 .matchEthType(TYPE_IPV4)
271 .matchIPDst(remotePrefix)
272 .build();
273 // treatment
274 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
275 .setOutput(tunnelPort.number())
276 .build();
277 // forwarding objective builder
278 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
279 .withSelector(selector)
280 .withTreatment(treatment)
281 .withPriority(PRIORITY)
282 .withFlag(ForwardingObjective.Flag.VERSATILE)
283 .fromApp(appId)
284 .add();
285 // send flow objective to specified switch
286 flowObjectiveService.forward(DeviceId.deviceId(tunnelPort.element().id().toString()),
287 forwardingObjective);
288
289 log.info("Installing flow rule = {}", forwardingObjective);
290
291 // selector
292 selector = DefaultTrafficSelector.builder()
293 .matchEthType(TYPE_IPV4)
294 .matchIPSrc(config.moasInfo().getTunnelPoint().getLocalIp().toIpPrefix())
295 .matchIPDst(remoteTunnelIp.toIpPrefix())
296 .build();
297 // treatment
298 treatment = DefaultTrafficTreatment.builder()
299 // FIXME: find a better way
300 .setOutput(PortNumber.portNumber(2))
301 .build();
302 // forwarding objective builder
303 forwardingObjective = DefaultForwardingObjective.builder()
304 .withSelector(selector)
305 .withTreatment(treatment)
306 .withPriority(PRIORITY)
307 .withFlag(ForwardingObjective.Flag.VERSATILE)
308 .fromApp(appId)
309 .add();
310 // send flow objective to specified switch
311 flowObjectiveService.forward(DeviceId.deviceId(tunnelPort.element().id().toString()),
312 forwardingObjective);
313
314 log.info("Installing flow rule = {}", forwardingObjective);
315 });
316 }
317
318 /**
319 * Handles a artemis event.
320 *
321 * @param event the artemis event
322 */
323 protected void handleArtemisEvent(ArtemisEvent event) {
324 if (event.type().equals(ArtemisEvent.Type.HIJACK_ADDED)) {
325 IpPrefix receivedPrefix = (IpPrefix) event.subject();
326
327 log.info("Deaggregator received a prefix " + receivedPrefix.toString());
328
329 // can only de-aggregate /23 subnets and higher
330 int cidr = receivedPrefix.prefixLength();
331 if (receivedPrefix.prefixLength() < 24) {
332 byte[] octets = receivedPrefix.address().toOctets();
333 int byteGroup = (cidr + 1) / 8,
334 bitPos = 8 - (cidr + 1) % 8;
335
336 octets[byteGroup] = (byte) (octets[byteGroup] & ~(1 << bitPos));
337 String low = IpPrefix.valueOf(IpAddress.Version.INET, octets, cidr + 1).toString();
338 octets[byteGroup] = (byte) (octets[byteGroup] | (1 << bitPos));
339 String high = IpPrefix.valueOf(IpAddress.Version.INET, octets, cidr + 1).toString();
340
341 String[] prefixes = {low, high};
342 bgpSpeakers.forEach(bgpSpeakers -> bgpSpeakers.announceSubPrefixes(prefixes));
343 } else {
344 log.warn("Initiating MOAS");
345
346 artemisService.getConfig().ifPresent(config -> config.monitoredPrefixes().forEach(artemisPrefixes -> {
347 log.info("checking if {} > {}", artemisPrefixes.prefix(), receivedPrefix);
348 if (artemisPrefixes.prefix().contains(receivedPrefix)) {
349 artemisPrefixes.moas().forEach(moasAddress -> {
350 log.info("Creating a client for {}", moasAddress);
351 MoasClientController client = new MoasClientController(
352 packetProcessor,
353 moasAddress,
354 config.moasInfo().getTunnelPoints().iterator().next()
355 .getLocalIp(),
356 receivedPrefix);
357 log.info("Running client");
358 client.run();
359 moasClientControllers.add(client);
360 }
361 );
362 }
363 }
364 ));
365 }
366
367 }
368 }
369
370 private class InternalPacketProcessor implements ArtemisPacketProcessor {
371 @Override
372 public void processMoasPacket(ArtemisMessage msg, ChannelHandlerContext ctx) {
373 log.info("Received {}", msg);
374 switch (msg.getType()) {
375 case INITIATE_FROM_CLIENT: {
376 artemisService.getConfig().ifPresent(config -> {
377 // SERVER SIDE CODE
378 createTunnelInterface(IpAddress.valueOf(msg.getLocalIp()));
379
380 ArtemisMessage message = new ArtemisMessage();
381 message.setType(ArtemisMessage.Type.INITIATE_FROM_SERVER);
382 message.setLocalIp(
383 config.moasInfo().getTunnelPoints()
384 .iterator()
385 .next()
386 .getLocalIp()
387 .toString());
388
389 ObjectMapper mapper = new ObjectMapper();
390 try {
391 String jsonInString = mapper.writeValueAsString(message);
392 ByteBuf buffer = Unpooled.copiedBuffer(jsonInString, CharsetUtil.UTF_8);
393 ctx.writeAndFlush(buffer);
394 } catch (JsonProcessingException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800395 log.warn("processMoasPacket()", e);
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700396 }
397
398 remoteTunnelIp = IpAddress.valueOf(msg.getLocalIp());
399 remotePrefix = IpPrefix.valueOf(msg.getLocalPrefix());
400 });
401 break;
402 }
403 case INITIATE_FROM_SERVER: {
404 // CLIENT SIDE CODE
405 createTunnelInterface(IpAddress.valueOf(msg.getLocalIp()));
406
407 remoteTunnelIp = IpAddress.valueOf(msg.getLocalIp());
408
409 break;
410 }
411 default:
412 }
413
414 installRules();
415 }
416
417 @Override
418 public void processMonitorPacket(JSONObject msg) {
419
420 }
421 }
422
423 private class InternalMoasAgent implements ArtemisMoasAgent {
424
425 @Override
426 public void addMoas(IpAddress ipAddress, ChannelHandlerContext ctx) {
427 Optional<ArtemisConfig> config = artemisService.getConfig();
428 if (config.isPresent() && config.get().moasInfo().getMoasAddresses().contains(ipAddress)) {
429 log.info("Received Moas request from legit IP address");
430 } else {
431 log.info("Received Moas request from unknown IP address; ignoring..");
432 ctx.close();
433 }
434 }
435
436 @Override
437 public void removeMoas(IpAddress ipAddress) {
438
439 }
440 }
441
442 private class InternalDeviceListener implements DeviceListener {
443
444 /*
445 EVENT
446 DefaultDevice{id=of:000000000000000a, type=SWITCH, manufacturer=Nicira, Inc., hwVersion=Open vSwitch,
447 swVersion=2.8.0, serialNumber=None, driver=ovs}
448 DefaultPort{element=of:000000000000000a, number=5, isEnabled=true, type=COPPER, portSpeed=0, annotations=
449 {portMac=96:13:4c:12:ca:8a, portName=gre-int}}
450 */
451 @Override
452 public void event(DeviceEvent event) {
453 switch (event.type()) {
454 case PORT_UPDATED:
455 case PORT_ADDED: {
456 log.info("event {}", event);
457 // FIXME: currently only one tunnel is supported
458 if (event.port().annotations().keys().contains("portName") &&
459 event.port().annotations().value("portName").equals("gre-int")) {
460 tunnelPort = event.port();
461
462 installRules();
463 }
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800464 break;
Dimitrios Mavrommatisf0c06322017-10-31 23:49:04 -0700465 }
466 default:
467 }
468 }
469 }
470}