Jonathan Hart

Initial skeleton of BgpRouter app.

Added BGP tunnelling and received routes.
Added pushing groups and can now ping through the router.

Change-Id: I21a265bd72e40fc430bd392201fadccbdd67be94
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2015 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20 + <parent>
21 + <artifactId>onos-apps</artifactId>
22 + <groupId>org.onosproject</groupId>
23 + <version>1.1.0-SNAPSHOT</version>
24 + <relativePath>../pom.xml</relativePath>
25 + </parent>
26 + <modelVersion>4.0.0</modelVersion>
27 +
28 + <artifactId>onos-app-bgprouter</artifactId>
29 +
30 + <packaging>bundle</packaging>
31 + <description>BGP router application</description>
32 +
33 + <dependencies>
34 + <dependency>
35 + <groupId>org.onosproject</groupId>
36 + <artifactId>onos-app-routing-api</artifactId>
37 + <version>${project.version}</version>
38 + </dependency>
39 +
40 + <dependency>
41 + <groupId>org.onosproject</groupId>
42 + <artifactId>onos-cli</artifactId>
43 + <version>${project.version}</version>
44 + </dependency>
45 +
46 + <dependency>
47 + <groupId>com.google.guava</groupId>
48 + <artifactId>guava</artifactId>
49 + </dependency>
50 +
51 + <dependency>
52 + <groupId>org.osgi</groupId>
53 + <artifactId>org.osgi.compendium</artifactId>
54 + </dependency>
55 +
56 + <dependency>
57 + <groupId>org.osgi</groupId>
58 + <artifactId>org.osgi.core</artifactId>
59 + </dependency>
60 + </dependencies>
61 +</project>
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.bgprouter;
17 +
18 +import com.google.common.collect.ConcurrentHashMultiset;
19 +import com.google.common.collect.Multiset;
20 +import org.apache.felix.scr.annotations.Activate;
21 +import org.apache.felix.scr.annotations.Component;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.onlab.packet.Ethernet;
26 +import org.onosproject.core.ApplicationId;
27 +import org.onosproject.core.CoreService;
28 +import org.onosproject.net.DeviceId;
29 +import org.onosproject.net.flow.DefaultFlowRule;
30 +import org.onosproject.net.flow.DefaultTrafficSelector;
31 +import org.onosproject.net.flow.DefaultTrafficTreatment;
32 +import org.onosproject.net.flow.FlowRule;
33 +import org.onosproject.net.flow.FlowRuleService;
34 +import org.onosproject.net.flow.TrafficSelector;
35 +import org.onosproject.net.flow.TrafficTreatment;
36 +import org.onosproject.net.group.DefaultGroupBucket;
37 +import org.onosproject.net.group.DefaultGroupDescription;
38 +import org.onosproject.net.group.Group;
39 +import org.onosproject.net.group.GroupBucket;
40 +import org.onosproject.net.group.GroupBuckets;
41 +import org.onosproject.net.group.GroupDescription;
42 +import org.onosproject.net.group.GroupKey;
43 +import org.onosproject.net.group.GroupService;
44 +import org.onosproject.net.packet.PacketService;
45 +import org.onosproject.routingapi.FibListener;
46 +import org.onosproject.routingapi.FibUpdate;
47 +import org.onosproject.routingapi.RoutingService;
48 +import org.onosproject.routingapi.config.Interface;
49 +import org.onosproject.routingapi.config.RoutingConfigurationService;
50 +import org.slf4j.Logger;
51 +import org.slf4j.LoggerFactory;
52 +
53 +import java.util.Collection;
54 +import java.util.Collections;
55 +import java.util.HashMap;
56 +import java.util.Map;
57 +
58 +/**
59 + * BgpRouter component.
60 + */
61 +@Component(immediate = true)
62 +public class BgpRouter {
63 +
64 + private static final Logger log = LoggerFactory.getLogger(BgpRouter.class);
65 +
66 + private static final String BGP_ROUTER_APP = "org.onosproject.bgprouter";
67 +
68 + private static final int PRIORITY = 1;
69 +
70 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 + protected CoreService coreService;
72 +
73 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 + protected FlowRuleService flowService;
75 +
76 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 + protected GroupService groupService;
78 +
79 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 + protected RoutingService routingService;
81 +
82 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 + protected RoutingConfigurationService configService;
84 +
85 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 + protected PacketService packetService;
87 +
88 + private ApplicationId appId;
89 +
90 + private final Multiset<NextHop> nextHops = ConcurrentHashMultiset.create();
91 + private final Map<NextHop, NextHopGroupKey> groups = new HashMap<>();
92 +
93 + private DeviceId deviceId = DeviceId.deviceId("of:00000000000000a1"); // TODO config
94 +
95 + private TunnellingConnectivityManager connectivityManager;
96 +
97 + @Activate
98 + protected void activate() {
99 + log.info("Bgp1Router started");
100 + appId = coreService.registerApplication(BGP_ROUTER_APP);
101 +
102 + connectivityManager = new TunnellingConnectivityManager(appId,
103 + configService,
104 + packetService);
105 +
106 + routingService.start(new InternalFibListener());
107 +
108 + connectivityManager.start();
109 +
110 + log.info("BgpRouter started");
111 + }
112 +
113 + @Deactivate
114 + protected void deactivate() {
115 + routingService.stop();
116 + connectivityManager.stop();
117 +
118 + log.info("BgpRouter stopped");
119 + }
120 +
121 + private void updateFibEntry(Collection<FibUpdate> updates) {
122 + for (FibUpdate update : updates) {
123 + NextHop nextHop = new NextHop(update.entry().nextHopIp(),
124 + update.entry().nextHopMac());
125 +
126 + addNextHop(nextHop);
127 +
128 + TrafficSelector selector = DefaultTrafficSelector.builder()
129 + .matchEthType(Ethernet.TYPE_IPV4)
130 + .matchIPDst(update.entry().prefix())
131 + .build();
132 +
133 + // TODO ensure group exists
134 + NextHopGroupKey groupKey = groups.get(nextHop);
135 + Group group = groupService.getGroup(deviceId, groupKey);
136 + if (group == null) {
137 + // TODO handle this
138 + log.warn("oops, group {} wasn't there");
139 + continue;
140 + }
141 +
142 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
143 + .group(group.id())
144 + .build();
145 +
146 + FlowRule flowRule = new DefaultFlowRule(deviceId, selector, treatment,
147 + PRIORITY, appId, 0, true,
148 + FlowRule.Type.IP);
149 +
150 + flowService.applyFlowRules(flowRule);
151 + }
152 + }
153 +
154 + private void deleteFibEntry(Collection<FibUpdate> withdraws) {
155 + for (FibUpdate update : withdraws) {
156 + NextHop nextHop = new NextHop(update.entry().nextHopIp(),
157 + update.entry().nextHopMac());
158 +
159 + deleteNextHop(nextHop);
160 +
161 + TrafficSelector selector = DefaultTrafficSelector.builder()
162 + .matchIPDst(update.entry().prefix())
163 + .build();
164 +
165 + FlowRule flowRule = new DefaultFlowRule(deviceId, selector, null,
166 + PRIORITY, appId, 0, true,
167 + FlowRule.Type.IP);
168 +
169 + flowService.removeFlowRules(flowRule);
170 + }
171 + }
172 +
173 + private void addNextHop(NextHop nextHop) {
174 + if (nextHops.add(nextHop, 1) == 0) {
175 + // There was no next hop in the multiset
176 +
177 + Interface egressIntf = configService.getMatchingInterface(nextHop.ip());
178 + if (egressIntf == null) {
179 + log.warn("no egress interface found for {}", nextHop);
180 + return;
181 + }
182 +
183 + NextHopGroupKey groupKey = new NextHopGroupKey(nextHop.ip());
184 + groups.put(nextHop, groupKey);
185 +
186 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
187 + .setEthSrc(egressIntf.mac())
188 + .setEthDst(nextHop.mac())
189 + .setVlanId(egressIntf.vlan())
190 + .setOutput(egressIntf.connectPoint().port())
191 + .build();
192 +
193 + GroupBucket bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
194 +
195 + GroupDescription groupDescription
196 + = new DefaultGroupDescription(deviceId,
197 + GroupDescription.Type.INDIRECT,
198 + new GroupBuckets(Collections
199 + .singletonList(bucket)),
200 + groupKey,
201 + appId);
202 +
203 + groupService.addGroup(groupDescription);
204 + }
205 + }
206 +
207 + private void deleteNextHop(NextHop nextHop) {
208 + if (nextHops.remove(nextHop, 1) <= 1) {
209 + // There was one or less next hops, so there are now none
210 +
211 + log.debug("removing group");
212 +
213 + GroupKey groupKey = groups.remove(nextHop);
214 + groupService.removeGroup(deviceId, groupKey, appId);
215 + }
216 + }
217 +
218 + private class InternalFibListener implements FibListener {
219 +
220 + @Override
221 + public void update(Collection<FibUpdate> updates,
222 + Collection<FibUpdate> withdraws) {
223 + BgpRouter.this.deleteFibEntry(withdraws);
224 + BgpRouter.this.updateFibEntry(updates);
225 + }
226 + }
227 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.bgprouter;
17 +
18 +import com.google.common.base.MoreObjects;
19 +import org.onlab.packet.IpAddress;
20 +import org.onlab.packet.MacAddress;
21 +
22 +import java.util.Objects;
23 +
24 +/**
25 + * Created by jono on 2/12/15.
26 + */
27 +public class NextHop {
28 +
29 + private final IpAddress ip;
30 + private final MacAddress mac;
31 +
32 + public NextHop(IpAddress ip, MacAddress mac) {
33 + this.ip = ip;
34 + this.mac = mac;
35 + }
36 +
37 + public IpAddress ip() {
38 + return ip;
39 + }
40 +
41 + public MacAddress mac() {
42 + return mac;
43 + }
44 +
45 + @Override
46 + public boolean equals(Object o) {
47 + if (!(o instanceof NextHop)) {
48 + return false;
49 + }
50 +
51 + NextHop that = (NextHop) o;
52 +
53 + return Objects.equals(this.ip, that.ip) &&
54 + Objects.equals(this.mac, that.mac);
55 + }
56 +
57 + @Override
58 + public int hashCode() {
59 + return Objects.hash(ip, mac);
60 + }
61 +
62 + @Override
63 + public String toString() {
64 + return MoreObjects.toStringHelper(getClass())
65 + .add("ip", ip)
66 + .add("mac", mac)
67 + .toString();
68 + }
69 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.bgprouter;
17 +
18 +import com.google.common.base.MoreObjects;
19 +import org.onlab.packet.IpAddress;
20 +import org.onosproject.net.group.GroupKey;
21 +
22 +import java.util.Objects;
23 +
24 +import static com.google.common.base.Preconditions.checkNotNull;
25 +
26 +/**
27 + * Created by jono on 2/16/15.
28 + */
29 +public class NextHopGroupKey implements GroupKey {
30 +
31 + private final IpAddress address;
32 +
33 + public NextHopGroupKey(IpAddress address) {
34 + this.address = checkNotNull(address);
35 + }
36 +
37 + public IpAddress address() {
38 + return address;
39 + }
40 +
41 + @Override
42 + public boolean equals(Object o) {
43 + if (!(o instanceof NextHopGroupKey)) {
44 + return false;
45 + }
46 +
47 + NextHopGroupKey that = (NextHopGroupKey) o;
48 +
49 + return Objects.equals(this.address, that.address);
50 + }
51 +
52 + @Override
53 + public int hashCode() {
54 + return Objects.hash(address);
55 + }
56 +
57 + @Override
58 + public String toString() {
59 + return MoreObjects.toStringHelper(getClass())
60 + .add("address", address)
61 + .toString();
62 + }
63 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.bgprouter;
17 +
18 +import org.onlab.packet.Ethernet;
19 +import org.onlab.packet.IPv4;
20 +import org.onlab.packet.IpAddress;
21 +import org.onlab.packet.TCP;
22 +import org.onosproject.core.ApplicationId;
23 +import org.onosproject.net.ConnectPoint;
24 +import org.onosproject.net.flow.DefaultTrafficSelector;
25 +import org.onosproject.net.flow.DefaultTrafficTreatment;
26 +import org.onosproject.net.flow.TrafficSelector;
27 +import org.onosproject.net.flow.TrafficTreatment;
28 +import org.onosproject.net.packet.DefaultOutboundPacket;
29 +import org.onosproject.net.packet.OutboundPacket;
30 +import org.onosproject.net.packet.PacketContext;
31 +import org.onosproject.net.packet.PacketPriority;
32 +import org.onosproject.net.packet.PacketProcessor;
33 +import org.onosproject.net.packet.PacketService;
34 +import org.onosproject.routingapi.config.BgpPeer;
35 +import org.onosproject.routingapi.config.BgpSpeaker;
36 +import org.onosproject.routingapi.config.InterfaceAddress;
37 +import org.onosproject.routingapi.config.RoutingConfigurationService;
38 +import org.slf4j.Logger;
39 +import org.slf4j.LoggerFactory;
40 +
41 +
42 +/**
43 + * Manages connectivity between peers by tunnelling BGP traffic through
44 + * OpenFlow packet-ins and packet-outs.
45 + */
46 +public class TunnellingConnectivityManager {
47 +
48 + private static final short BGP_PORT = 179;
49 +
50 + private final ApplicationId appId;
51 +
52 + private final PacketService packetService;
53 + private final RoutingConfigurationService configService;
54 +
55 + private final BgpProcessor processor = new BgpProcessor();
56 +
57 + public TunnellingConnectivityManager(ApplicationId appId,
58 + RoutingConfigurationService configService,
59 + PacketService packetService) {
60 + this.appId = appId;
61 + this.configService = configService;
62 + this.packetService = packetService;
63 + }
64 +
65 + public void start() {
66 + packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 3);
67 +
68 + TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
69 +
70 + // Request packets with BGP port as their TCP source port
71 + selector.matchEthType(Ethernet.TYPE_IPV4);
72 + selector.matchIPProtocol(IPv4.PROTOCOL_TCP);
73 + selector.matchTcpSrc(BGP_PORT);
74 +
75 + packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
76 + appId);
77 +
78 + selector = DefaultTrafficSelector.builder();
79 +
80 + // Request packets with BGP port as their TCP destination port
81 + selector.matchEthType(Ethernet.TYPE_IPV4);
82 + selector.matchIPProtocol(IPv4.PROTOCOL_TCP);
83 + selector.matchTcpDst(BGP_PORT);
84 +
85 + packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
86 + appId);
87 + }
88 +
89 + public void stop() {
90 + packetService.removeProcessor(processor);
91 + // Should revoke packet requests in the future
92 + }
93 +
94 + /**
95 + * Forwards a BGP packet to another connect point.
96 + *
97 + * @param context the packet context of the incoming packet
98 + */
99 + private void forward(PacketContext context) {
100 +
101 + ConnectPoint outputPort = null;
102 + Logger log = LoggerFactory.getLogger(getClass());
103 +
104 +
105 + IPv4 ipv4 = (IPv4) context.inPacket().parsed().getPayload();
106 + IpAddress dstAddress = IpAddress.valueOf(ipv4.getDestinationAddress());
107 +
108 + for (BgpSpeaker speaker : configService.getBgpSpeakers().values()) {
109 + if (context.inPacket().receivedFrom().equals(speaker.connectPoint())) {
110 + BgpPeer peer = configService.getBgpPeers().get(dstAddress);
111 + if (peer != null) {
112 + outputPort = peer.connectPoint();
113 + }
114 + break;
115 + }
116 + for (InterfaceAddress addr : speaker.interfaceAddresses()) {
117 + if (addr.ipAddress().equals(dstAddress) && !context.inPacket()
118 + .receivedFrom().equals(speaker.connectPoint())) {
119 + outputPort = speaker.connectPoint();
120 + }
121 + }
122 + }
123 +
124 + if (outputPort != null) {
125 + TrafficTreatment t = DefaultTrafficTreatment.builder()
126 + .setOutput(outputPort.port()).build();
127 + OutboundPacket o = new DefaultOutboundPacket(
128 + outputPort.deviceId(), t, context.inPacket().unparsed());
129 + packetService.emit(o);
130 + }
131 + }
132 +
133 + /**
134 + * Packet processor responsible receiving and filtering BGP packets.
135 + */
136 + private class BgpProcessor implements PacketProcessor {
137 +
138 + @Override
139 + public void process(PacketContext context) {
140 + // Stop processing if the packet has been handled, since we
141 + // can't do any more to it.
142 + if (context.isHandled()) {
143 + return;
144 + }
145 +
146 + Ethernet packet = context.inPacket().parsed();
147 +
148 + if (packet == null) {
149 + return;
150 + }
151 +
152 + if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
153 + IPv4 ipv4Packet = (IPv4) packet.getPayload();
154 + if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_TCP) {
155 + TCP tcpPacket = (TCP) ipv4Packet.getPayload();
156 +
157 + if (tcpPacket.getDestinationPort() == BGP_PORT ||
158 + tcpPacket.getSourcePort() == BGP_PORT) {
159 + forward(context);
160 + }
161 + }
162 + }
163 + }
164 + }
165 +}
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
47 <module>election</module> 47 <module>election</module>
48 <module>routing</module> 48 <module>routing</module>
49 <module>routing-api</module> 49 <module>routing-api</module>
50 + <module>bgprouter</module>
50 </modules> 51 </modules>
51 52
52 <properties> 53 <properties>
......
...@@ -215,6 +215,16 @@ ...@@ -215,6 +215,16 @@
215 <bundle>mvn:org.onosproject/onos-app-routing/@ONOS-VERSION</bundle> 215 <bundle>mvn:org.onosproject/onos-app-routing/@ONOS-VERSION</bundle>
216 </feature> 216 </feature>
217 217
218 + <feature name="onos-app-bgprouter" version="@FEATURE-VERSION"
219 + description="BGP router application">
220 + <feature>onos-api</feature>
221 + <feature>onos-app-proxyarp</feature>
222 + <feature>onos-app-config</feature>
223 + <bundle>mvn:org.onosproject/onos-app-bgprouter/@ONOS-VERSION</bundle>
224 + <bundle>mvn:org.onosproject/onos-app-routing-api/@ONOS-VERSION</bundle>
225 + <bundle>mvn:org.onosproject/onos-app-routing/@ONOS-VERSION</bundle>
226 + </feature>
227 +
218 <feature name="onos-app-calendar" version="@FEATURE-VERSION" 228 <feature name="onos-app-calendar" version="@FEATURE-VERSION"
219 description="REST interface for scheduling intents from an external calendar"> 229 description="REST interface for scheduling intents from an external calendar">
220 <feature>onos-api</feature> 230 <feature>onos-api</feature>
......