1839c1b68b80118ce1dc7de9525194f187e65c85
[vtn.git] /
1 /*
2  * Copyright (c) 2015 NEC Corporation
3  * All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this
7  * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.vtn.manager.internal.packet;
11
12 import java.util.Iterator;
13 import java.util.TimerTask;
14 import java.util.concurrent.ConcurrentHashMap;
15 import java.util.concurrent.ConcurrentMap;
16 import java.util.concurrent.CopyOnWriteArrayList;
17 import java.util.concurrent.Future;
18 import java.util.concurrent.atomic.AtomicReference;
19
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
24 import org.opendaylight.vtn.manager.internal.inventory.VTNInventoryListener;
25 import org.opendaylight.vtn.manager.internal.inventory.VtnNodeEvent;
26 import org.opendaylight.vtn.manager.internal.inventory.VtnPortEvent;
27 import org.opendaylight.vtn.manager.internal.util.SalNode;
28 import org.opendaylight.vtn.manager.internal.util.SalNotificationListener;
29 import org.opendaylight.vtn.manager.internal.util.SalPort;
30 import org.opendaylight.vtn.manager.internal.util.concurrent.FutureErrorCallback;
31
32 import org.opendaylight.controller.sal.binding.api.NotificationService;
33
34 import org.opendaylight.yangtools.yang.common.RpcResult;
35
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
41
42 import org.opendaylight.controller.sal.packet.Packet;
43 import org.opendaylight.controller.sal.utils.EtherTypes;
44
45 /**
46  * Provider of internal packet services.
47  */
48 public final class VTNPacketService extends SalNotificationListener
49     implements PacketProcessingListener, VTNInventoryListener {
50     /**
51      * Logger instance.
52      */
53     private static final Logger  LOG =
54         LoggerFactory.getLogger(VTNPacketService.class);
55
56     /**
57      * VTN Manager provider service.
58      */
59     private final VTNManagerProvider  vtnProvider;
60
61     /**
62      * MD-SAL packet processing service.
63      */
64     private final AtomicReference<PacketProcessingService>  packetService =
65         new AtomicReference<PacketProcessingService>();
66
67     /**
68      * A list of VTN packet listeners.
69      */
70     private final CopyOnWriteArrayList<VTNPacketListener>  listeners =
71         new CopyOnWriteArrayList<VTNPacketListener>();
72
73     /**
74      * Keep nodes which are not in service yet.
75      *
76      * <p>
77      *   If a node is contained in this map, any packet from the node is
78      *   ignored, and no packet is sent to the node.
79      * </p>
80      */
81     private ConcurrentMap<SalNode, TimerTask>  disabledNodes =
82         new ConcurrentHashMap<SalNode, TimerTask>();
83
84     /**
85      * Construct a new instance.
86      *
87      * @param provider  A VTN Manager provider service.
88      * @param nsv       A {@link NotificationService} service instance.
89      */
90     public VTNPacketService(VTNManagerProvider provider,
91                             NotificationService nsv) {
92         vtnProvider = provider;
93
94         try {
95             // Get MD-SAL packet processing service.
96             PacketProcessingService pps = provider.
97                 getRpcService(PacketProcessingService.class);
98             packetService.set(pps);
99
100             // Register MD-SAL notification listener.
101             registerListener(nsv);
102         } catch (Exception e) {
103             String msg = "Failed to initialize VTN packet service.";
104             LOG.error(msg, e);
105             close();
106             throw new IllegalStateException(msg, e);
107         }
108     }
109
110     /**
111      * Add the given VTN packet listener.
112      *
113      * @param l  A VTN packet listener.
114      */
115     public void addListener(VTNPacketListener l) {
116         listeners.addIfAbsent(l);
117     }
118
119     /**
120      * Transmit the given packet.
121      *
122      * @param egress  A {@link SalPort} instance which specifies the egress
123      *                switch port.
124      * @param packet  A {@link Packet} instance to be transmitted.
125      */
126     public void transmit(SalPort egress, Packet packet) {
127         PacketProcessingService pps = packetService.get();
128         if (pps == null) {
129             return;
130         }
131
132         byte[] payload;
133         try {
134             payload = packet.serialize();
135         } catch (Exception e) {
136             LOG.error("Failed to transmit packet via " + egress, e);
137             return;
138         }
139
140         TransmitPacketInputBuilder builder = new TransmitPacketInputBuilder();
141         builder.setNode(egress.getNodeRef()).
142             setEgress(egress.getNodeConnectorRef()).
143             setPayload(payload);
144
145         Future<RpcResult<Void>> f = pps.transmitPacket(builder.build());
146         FutureErrorCallback<RpcResult<Void>> cb = new FutureErrorCallback<>(
147             LOG, "Failed to transmit packet.");
148         vtnProvider.setCallback(f, cb);
149     }
150
151     /**
152      * Add the given node to {@link #disabledNodes}.
153      *
154      * <p>
155      *   Neighbor node discovery may not be completed on a newly detected node.
156      *   If a PACKET_OUT is sent to a port in a newly detected node, it may
157      *   cause broadcast packet storm because the controller can not
158      *   determine internal port in a node yet.
159      * </p>
160      * <p>
161      *   That is why we should disable any packet service on a newly detected
162      *   node for a while.
163      * </p>
164      *
165      * @param snode  A newly detected node.
166      */
167     private void addDisabledNode(final SalNode snode) {
168         int edgeWait = vtnProvider.getVTNConfig().getNodeEdgeWait();
169         if (edgeWait <= 0) {
170             return;
171         }
172
173         if (packetService.get() == null) {
174             // Packet service is already closed.
175             return;
176         }
177
178         // Create a timer task to remove the node from disabledNodes.
179         TimerTask task = new TimerTask() {
180             @Override
181             public void run() {
182                 if (disabledNodes.remove(snode) != null) {
183                     LOG.info("{}: Start packet service", snode);
184                 }
185             }
186         };
187
188         if (disabledNodes.putIfAbsent(snode, task) == null) {
189             vtnProvider.getTimer().schedule(task, edgeWait);
190         }
191     }
192
193     // AutoCloseable
194
195     /**
196      * Close the internal packet service.
197      */
198     @Override
199     public void close() {
200         listeners.clear();
201         super.close();
202         packetService.set(null);
203
204         for (Iterator<TimerTask> it = disabledNodes.values().iterator();
205              it.hasNext();) {
206             TimerTask task = it.next();
207             task.cancel();
208             it.remove();
209         }
210     }
211
212     // PacketProcessingListener
213
214     /**
215      * Invoked when a PACKET_IN message has been notified.
216      *
217      * @param notification  A PACKET_IN notification.
218      */
219     @Override
220     public void onPacketReceived(PacketReceived notification) {
221         if (notification == null) {
222             LOG.debug("Ignore null notification.");
223             return;
224         }
225
226         NodeConnectorRef nref = notification.getIngress();
227         SalPort ingress = SalPort.create(nref);
228         if (ingress == null) {
229             LOG.trace("Ignore packet received on unsupported port: {}", nref);
230             return;
231         }
232
233         SalNode snode = ingress.getSalNode();
234         if (disabledNodes.containsKey(snode)) {
235             LOG.trace("Ignore packet from disabled node: {}", snode);
236             return;
237         }
238
239         PacketInEvent ev = null;
240         for (VTNPacketListener l: listeners) {
241             if (ev == null) {
242                 try {
243                     ev = new PacketInEvent(l, notification, ingress);
244                 } catch (IllegalArgumentException e) {
245                     LOG.debug("Ignore received packet: {}", e.getMessage());
246                     return;
247                 } catch (Exception e) {
248                     LOG.error("Failed to create PacketInEvent: " +
249                               notification, e);
250                     return;
251                 }
252
253                 if (ev.getEthernet().getEtherType() ==
254                     EtherTypes.LLDP.shortValue()) {
255                     // Ignore LLDP packet.
256                     return;
257                 }
258             } else {
259                 ev = new PacketInEvent(l, ev);
260             }
261
262             vtnProvider.post(ev);
263         }
264     }
265
266     // SalNotificationListener
267
268     /**
269      * {@inheritDoc}
270      */
271     @Override
272     protected Logger getLogger() {
273         return LOG;
274     }
275
276     // VTNInventoryListener
277
278     /**
279      * {@inheritDoc}
280      */
281     @Override
282     public void notifyVtnNode(VtnNodeEvent ev) {
283         switch (ev.getUpdateType()) {
284         case CREATED:
285             addDisabledNode(ev.getSalNode());
286             break;
287
288         case REMOVED:
289             TimerTask task = disabledNodes.get(ev.getSalNode());
290             if (task != null) {
291                 task.cancel();
292             }
293             break;
294
295         default:
296             break;
297         }
298     }
299
300     /**
301      * {@inheritDoc}
302      */
303     @Override
304     public void notifyVtnPort(VtnPortEvent ev) {
305         // Nothing to do.
306     }
307 }