blob: 3d6e7d0cbf24bc48f01772f64a43b589f3c825e3 [file] [log] [blame]
Himal Kumarb43724d2016-04-29 14:15:57 +10001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Himal Kumarb43724d2016-04-29 14:15:57 +10003 *
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.castor;
17
Himal Kumarb43724d2016-04-29 14:15:57 +100018import org.onlab.packet.ARP;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.VlanId;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.flow.DefaultTrafficSelector;
29import org.onosproject.net.flow.DefaultTrafficTreatment;
30import org.onosproject.net.flow.TrafficSelector;
31import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.packet.DefaultOutboundPacket;
33import org.onosproject.net.packet.InboundPacket;
34import org.onosproject.net.packet.PacketContext;
35import org.onosproject.net.packet.PacketProcessor;
36import org.onosproject.net.packet.PacketService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070037import org.osgi.service.component.annotations.Activate;
38import org.osgi.service.component.annotations.Component;
39import org.osgi.service.component.annotations.Deactivate;
40import org.osgi.service.component.annotations.Reference;
41import org.osgi.service.component.annotations.ReferenceCardinality;
Himal Kumarb43724d2016-04-29 14:15:57 +100042import org.slf4j.Logger;
43
44import java.nio.ByteBuffer;
45import java.util.Optional;
46import java.util.Set;
47
48import static org.onlab.packet.Ethernet.TYPE_ARP;
49import static org.onosproject.net.packet.PacketPriority.CONTROL;
50import static org.slf4j.LoggerFactory.getLogger;
51
52/**
53 * Component for managing the ARPs.
54 */
55
Ray Milkeyd84f89b2018-08-17 14:54:17 -070056@Component(immediate = true, service = ArpService.class)
Himal Kumarb43724d2016-04-29 14:15:57 +100057public class CastorArpManager implements ArpService {
58
Ray Milkeyd84f89b2018-08-17 14:54:17 -070059 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100060 protected ConnectivityManagerService connectivityManager;
61
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100063 protected PacketService packetService;
64
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100066 protected CoreService coreService;
67
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100069 protected CastorStore castorStore;
70
71 private ProxyArpProcessor processor = new ProxyArpProcessor();
72
73 private final Logger log = getLogger(getClass());
74 private static final int FLOW_PRIORITY = 500;
75 private static final MacAddress ARP_SOURCEMAC = MacAddress.valueOf("00:00:00:00:00:01");
76 private static final MacAddress ARP_DEST = MacAddress.valueOf("00:00:00:00:00:00");
77 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
78 private static final IpAddress ARP_SRC = Ip4Address.valueOf("0.0.0.0");
79
80 private ApplicationId appId;
81 Optional<DeviceId> deviceID = null;
82
83 private enum Protocol {
84 ARP
85 }
86
87 private enum MessageType {
88 REQUEST, REPLY
89 }
90
91 @Activate
92 public void activate() {
93 appId = coreService.getAppId(Castor.CASTOR_APP);
94 packetService.addProcessor(processor, PacketProcessor.director(1));
95 requestPackets();
96 }
97
98 @Deactivate
99 public void deactivate() {
100 withdrawIntercepts();
101 packetService.removeProcessor(processor);
102 processor = null;
103 }
104
105 /**
106 * Used to request the ARP packets.
107 */
108 private void requestPackets() {
109 TrafficSelector.Builder selectorBuilder =
110 DefaultTrafficSelector.builder();
111 selectorBuilder.matchEthType(TYPE_ARP);
112 packetService.requestPackets(selectorBuilder.build(), CONTROL, appId);
113 }
114
115 /**
116 * Withdraws the requested ARP packets.
117 */
118 private void withdrawIntercepts() {
119 TrafficSelector.Builder selectorBuilder =
120 DefaultTrafficSelector.builder();
121 selectorBuilder.matchEthType(TYPE_ARP);
122 packetService.cancelPackets(selectorBuilder.build(), CONTROL, appId, deviceID);
123 }
124
125 /**
126 * Forwards the ARP packet to the specified connect point via packet out.
127 *
128 * @param context The packet context
129 */
130 private void forward(MessageContext context) {
131
132 TrafficTreatment.Builder builder = null;
133 Ethernet eth = context.packet();
134 ByteBuffer buf = ByteBuffer.wrap(eth.serialize());
135
136 IpAddress target = context.target();
137 String value = getMatchingConnectPoint(target);
138 if (value != null) {
139 ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(value);
140 builder = DefaultTrafficTreatment.builder();
141 builder.setOutput(connectPoint.port());
142 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(), builder.build(), buf));
143 }
144 }
145
146 @Override
147 public void createArp(Peer peer) {
148
149 Ethernet packet = null;
150 packet = buildArpRequest(peer);
151 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
152 ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(peer.getPort());
153
154 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
155 builder.setOutput(connectPoint.port());
156 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(), builder.build(), buf));
157
158 }
159
160 /**
161 * Builds the ARP request when MAC is not known.
162 *
163 * @param peer The Peer whose MAC is not known.
164 * @return Ethernet
165 */
166 private Ethernet buildArpRequest(Peer peer) {
167 ARP arp = new ARP();
168 arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
169 .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
170 .setProtocolType(ARP.PROTO_TYPE_IP)
171 .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
172 .setOpCode(ARP.OP_REQUEST);
173
174 arp.setSenderHardwareAddress(ARP_SOURCEMAC.toBytes())
175 .setSenderProtocolAddress(ARP_SRC.toOctets())
176 .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
177 .setTargetProtocolAddress(IpAddress.valueOf(peer.getIpAddress()).toOctets());
178
179 Ethernet ethernet = new Ethernet();
180 ethernet.setEtherType(Ethernet.TYPE_ARP)
181 .setDestinationMACAddress(MacAddress.BROADCAST)
182 .setSourceMACAddress(ARP_SOURCEMAC)
183 .setPayload(arp);
184 ethernet.setPad(true);
185
186 return ethernet;
187 }
188
189 /**
190 * Gets the matching connect point corresponding to the peering IP address.
191 *
192 * @param target Target IP address
193 * @return Connect point as a String
194 */
195 private String getMatchingConnectPoint(IpAddress target) {
196 Set<Peer> peers = castorStore.getAllPeers();
197 for (Peer peer : peers) {
198 IpAddress match = IpAddress.valueOf(peer.getIpAddress());
199 if (match.equals(target)) {
200 return peer.getPort();
201 }
202 }
203 return null;
204 }
205
206 /**
207 * Returns the matching Peer or route server on a Connect Point.
208 *
209 * @param connectPoint The peering connect point.
210 * @return Peer or Route Server
211 */
212 private Peer getMatchingPeer(ConnectPoint connectPoint) {
213
214 for (Peer peer : castorStore.getAllPeers()) {
215 if (connectPoint.equals(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
216 return peer;
217 }
218 }
219 return null;
220 }
221
222 /**
223 * Returns matching BGP Peer on a connect point.
224 *
225 * @param connectPoint The peering connect point.
226 * @return The Peer
227 */
228 private Peer getMatchingCustomer(ConnectPoint connectPoint) {
229
230 for (Peer peer : castorStore.getCustomers()) {
231 if (connectPoint.equals(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
232 return peer;
233 }
234 }
235 return null;
236 }
237
238 /**
239 * Updates the IP address to mac address map.
240 *
241 * @param context The message context.
242 */
243 private void updateMac(MessageContext context) {
244
245 if ((castorStore.getAddressMap()).containsKey(context.sender())) {
246 return;
247 }
248 Ethernet eth = context.packet();
249 MacAddress macAddress = eth.getSourceMAC();
250 IpAddress ipAddress = context.sender();
251 castorStore.setAddressMap(ipAddress, macAddress);
252 }
253
254 /**
255 * Setup the layer two flows if not already installed after an ARP packet is received.
256 * If the layer 2 status is true, means layer two flows are already provisioned.
257 * If the status was false, layer 2 flows will be installed at this point. This
258 * happens when the mac address of a peer was not known at the time of its addition.
259 *
260 * @param msgContext The message context.
261 */
262 private void handleArpForL2(MessageContext msgContext) {
263
264 ConnectPoint cp = msgContext.inPort();
265 Peer peer = getMatchingCustomer(cp);
266
267 if (peer != null && !peer.getl2Status()) {
268 connectivityManager.setUpL2(peer);
269 }
270 }
271
272 @Override
273 public boolean handlePacket(PacketContext context) {
274
275 InboundPacket pkt = context.inPacket();
276 Ethernet ethPkt = pkt.parsed();
277
278 if (ethPkt == null) {
279 return false;
280 }
281
282 MessageContext msgContext = createContext(ethPkt, pkt.receivedFrom());
283
284 if (msgContext == null) {
285 return false;
286 }
287 switch (msgContext.type()) {
288 case REPLY:
289 forward(msgContext);
290 updateMac(msgContext);
291 handleArpForL2(msgContext);
292 break;
293 case REQUEST:
294 forward(msgContext);
295 updateMac(msgContext);
296 handleArpForL2(msgContext);
297 break;
298 default:
299 return false;
300 }
301 context.block();
302 return true;
303 }
304
305 private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
306 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
307 return createArpContext(eth, inPort);
308 }
309 return null;
310 }
311
312 /**
313 * Extracts context information from ARP packets.
314 *
315 * @param eth input Ethernet frame that is thought to be ARP
316 * @param inPort in port
317 * @return MessageContext object if the packet was a valid ARP packet,
318 * otherwise null
319 */
320 private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
321 if (eth.getEtherType() != Ethernet.TYPE_ARP) {
322 return null;
323 }
324
325 ARP arp = (ARP) eth.getPayload();
326
327 IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
328 IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
329
330 MessageType type;
331 if (arp.getOpCode() == ARP.OP_REQUEST) {
332 type = MessageType.REQUEST;
333 } else if (arp.getOpCode() == ARP.OP_REPLY) {
334 type = MessageType.REPLY;
335 } else {
336 return null;
337 }
338 return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
339 }
340
341 private class MessageContext {
342 private Protocol protocol;
343 private MessageType type;
344
345 private IpAddress target;
346 private IpAddress sender;
347
348 private Ethernet eth;
349 private ConnectPoint inPort;
350
351 public MessageContext(Ethernet eth, ConnectPoint inPort,
352 Protocol protocol, MessageType type,
353 IpAddress target, IpAddress sender) {
354 this.eth = eth;
355 this.inPort = inPort;
356 this.protocol = protocol;
357 this.type = type;
358 this.target = target;
359 this.sender = sender;
360 }
361
362 public ConnectPoint inPort() {
363 return inPort;
364 }
365
366 public Ethernet packet() {
367 return eth;
368 }
369
370 public Protocol protocol() {
371 return protocol;
372 }
373
374 public MessageType type() {
375 return type;
376 }
377
378 public VlanId vlan() {
379 return VlanId.vlanId(eth.getVlanID());
380 }
381
382 public MacAddress srcMac() {
383 return MacAddress.valueOf(eth.getSourceMACAddress());
384 }
385
386 public IpAddress target() {
387 return target;
388 }
389
390 public IpAddress sender() {
391 return sender;
392 }
393 }
394 private class ProxyArpProcessor implements PacketProcessor {
395
396 @Override
397 public void process(PacketContext context) {
398
399 if (context.isHandled()) {
400 return;
401 }
402 InboundPacket pkt = context.inPacket();
403 Ethernet ethPkt = pkt.parsed();
404 if (ethPkt == null) {
405 return;
406 }
407 if (ethPkt.getEtherType() == TYPE_ARP) {
408 //handle the arp packet.
409 handlePacket(context);
410 } else {
411 return;
412 }
413 }
414 }
415}