dee60a5b18f274b31dcd3678218fd67270b5bbf4
[vtn.git] /
1 /*
2  * Copyright (c) 2014, 2015 NEC Corporation.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.vtn.manager.internal.cluster;
10
11 import java.util.Set;
12 import java.util.concurrent.ConcurrentMap;
13
14 import org.slf4j.Logger;
15
16 import org.opendaylight.vtn.manager.IVTNManagerAware;
17 import org.opendaylight.vtn.manager.PortMap;
18 import org.opendaylight.vtn.manager.PortMapConfig;
19 import org.opendaylight.vtn.manager.SwitchPort;
20 import org.opendaylight.vtn.manager.VInterface;
21 import org.opendaylight.vtn.manager.VInterfaceConfig;
22 import org.opendaylight.vtn.manager.VInterfacePath;
23 import org.opendaylight.vtn.manager.VNodePath;
24 import org.opendaylight.vtn.manager.VNodeRoute;
25 import org.opendaylight.vtn.manager.VTNException;
26 import org.opendaylight.vtn.manager.VTenantPath;
27 import org.opendaylight.vtn.manager.util.EtherAddress;
28
29 import org.opendaylight.vtn.manager.internal.IVTNResourceManager;
30 import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
31 import org.opendaylight.vtn.manager.internal.PacketContext;
32 import org.opendaylight.vtn.manager.internal.TxContext;
33 import org.opendaylight.vtn.manager.internal.VTNManagerImpl;
34 import org.opendaylight.vtn.manager.internal.VTNConfig;
35 import org.opendaylight.vtn.manager.internal.VTNThreadData;
36 import org.opendaylight.vtn.manager.internal.inventory.VtnNodeEvent;
37 import org.opendaylight.vtn.manager.internal.inventory.VtnPortEvent;
38 import org.opendaylight.vtn.manager.internal.util.ProtocolUtils;
39 import org.opendaylight.vtn.manager.internal.util.flow.match.VTNEtherMatch;
40 import org.opendaylight.vtn.manager.internal.util.flow.match.VTNMatch;
41 import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
42 import org.opendaylight.vtn.manager.internal.util.inventory.InventoryUtils;
43 import org.opendaylight.vtn.manager.internal.util.inventory.NodeUtils;
44 import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
45 import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
46 import org.opendaylight.vtn.manager.internal.util.rpc.RpcException;
47 import org.opendaylight.vtn.manager.internal.util.flow.VTNFlowBuilder;
48
49 import org.opendaylight.controller.sal.core.Node;
50 import org.opendaylight.controller.sal.core.NodeConnector;
51 import org.opendaylight.controller.sal.core.UpdateType;
52 import org.opendaylight.controller.sal.packet.Ethernet;
53
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.VirtualRouteReason;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.node.info.VtnPort;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VnodeState;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnErrorTag;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
59
60 /**
61  * Implementation of virtual interface that can have port mapping
62  * configuration.
63  *
64  * <p>
65  *   Although this class is public to other packages, this class does not
66  *   provide any API. Applications other than VTN Manager must not use this
67  *   class.
68  * </p>
69  */
70 public abstract class PortInterface extends AbstractInterface
71     implements VirtualMapNode, FlowFilterNode {
72     /**
73      * Version number for serialization.
74      */
75     private static final long serialVersionUID = 3346949601083788579L;
76
77     /**
78      * Port mapping configuration.
79      */
80     private PortMapConfig  portMapConfig;
81
82     /**
83      * Flow filters for incoming packets.
84      */
85     private final FlowFilterMap  inFlowFilters;
86
87     /**
88      * Flow filters for outgoing packets.
89      */
90     private final FlowFilterMap  outFlowFilters;
91
92     /**
93      * Construct a virtual node instance that can have port mapping.
94      *
95      * @param parent  The parent node that contains this node.
96      * @param name    The name of this node.
97      * @param iconf   Configuration for the interface.
98      */
99     protected PortInterface(AbstractBridge parent, String name,
100                             VInterfaceConfig iconf) {
101         super(parent, name, iconf);
102         inFlowFilters = FlowFilterMap.createIncoming(this);
103         outFlowFilters = FlowFilterMap.createOutgoing(this);
104     }
105
106     /**
107      * Return the port mapping information.
108      *
109      * <p>
110      *   This method assumes that the call of this method is synchronized
111      *   by the parent node.
112      * </p>
113      *
114      * @param mgr  VTN Manager service.
115      * @return  Port mapping information. {@code null} is returned if the
116      *          port mapping is not configured on this node.
117      */
118     final PortMap getPortMap(VTNManagerImpl mgr) {
119         PortMapConfig pmconf = portMapConfig;
120         if (pmconf == null) {
121             return null;
122         }
123
124         return new PortMap(pmconf, getMappedPort(mgr));
125     }
126
127     /**
128      * Return a node connector associated with the switch port mapped to
129      * this node.
130      *
131      * <p>
132      *   This method assumes that the call of this method is synchronized
133      *   by the parent node.
134      * </p>
135      *
136      * @param mgr  VTN Manager service.
137      * @return  A node connector. {@code null} is returned if no swtich port
138      *          is mapped.
139      */
140     final NodeConnector getMappedPort(VTNManagerImpl mgr) {
141         VInterfaceState ist = getIfState(mgr);
142         return ist.getMappedPort();
143     }
144
145     /**
146      * Create or destroy mapping between the physical switch port and this
147      * virtual node.
148      *
149      * <p>
150      *   This method assumes that the call of this method is synchronized
151      *   by the parent node.
152      * </p>
153      *
154      * @param mgr     VTN Manager service.
155      * @param ctx     MD-SAL datastore transaction context.
156      * @param pmconf  Port mapping configuration to be set.
157      *                If {@code null} is specified, port mapping on the
158      *                specified interface is destroyed.
159      * @return  New state of this virtual interface.
160      *          {@code null} is returned if the port mapping configuration
161      *          was not changed.
162      * @throws VTNException  An error occurred.
163      */
164     final VnodeState setPortMap(VTNManagerImpl mgr, TxContext ctx,
165                                 PortMapConfig pmconf)
166         throws VTNException {
167         ConcurrentMap<VTenantPath, Object> db = mgr.getStateDB();
168         VInterfaceState ist = getIfState(db);
169
170         if (pmconf == null) {
171             // Destroy port mapping.
172             return unsetPortMap(mgr, db, ist);
173         }
174
175         PortMapConfig oldconf = portMapConfig;
176         if (pmconf.equals(oldconf)) {
177             // Nothing to do.
178             return null;
179         }
180
181         Node node = pmconf.getNode();
182         NodeUtils.checkNode(node);
183
184         short vlan = pmconf.getVlan();
185         ProtocolUtils.checkVlan(vlan);
186
187         SwitchPort port = pmconf.getPort();
188         NodeUtils.checkSwitchPort(port, node);
189
190         NodeConnector mapped = ist.getMappedPort();
191         IVTNResourceManager resMgr = mgr.getResourceManager();
192         PortVlan pvlan = null;
193
194         // Search for the switch port specified by the configuration.
195         InventoryReader reader = ctx.getInventoryReader();
196         NodeConnector nc;
197         SalPort sport = reader.findPort(node, port);
198         if (sport != null) {
199             nc = sport.getAdNodeConnector();
200             if (nc.equals(mapped) && oldconf.getVlan() == vlan) {
201                 // No need to change switch port mapping.
202                 portMapConfig = pmconf;
203                 return ist.getState();
204             }
205
206             pvlan = new PortVlan(nc, vlan);
207         } else {
208             nc = null;
209         }
210
211         PortVlan rmlan = (mapped == null)
212             ? null
213             : new PortVlan(mapped, oldconf.getVlan());
214
215         // Register port mapping, and unregister old port mapping if needed.
216         VInterfacePath path = getInterfacePath();
217         MapReference ref;
218         try {
219             ref = resMgr.registerPortMap(mgr, path, pvlan, rmlan, true);
220         } catch (VTNException e) {
221             VtnErrorTag etag = e.getVtnErrorTag();
222             if (etag == VtnErrorTag.INTERNALERROR) {
223                 mgr.logException(getLogger(), (VNodePath)path, e, pvlan,
224                                  rmlan);
225             }
226             throw e;
227         }
228
229         if (ref != null) {
230             assert ref.getMapType() == MapType.PORT;
231
232             throw RpcException.getDataExistsException(
233                 "Specified port is mapped to " + ref.getAbsolutePath());
234         }
235
236         // Update the state of this node.
237         portMapConfig = pmconf;
238         ist.setMappedPort(nc);
239         PortMap pmap = new PortMap(pmconf, nc);
240         if (oldconf == null) {
241             PortMapEvent.added(mgr, path, pmap);
242         } else {
243             PortMapEvent.changed(mgr, path, pmap, true);
244         }
245
246         VnodeState state;
247         if (isEnabled()) {
248             state = getNewState(reader, ist);
249         } else {
250             state = VnodeState.DOWN;
251             VnodeState pstate = getPortState(reader, sport);
252             ist.setPortState(pstate);
253         }
254         ist.setState(state);
255         db.put((VNodePath)path, ist);
256         if (ist.isDirty()) {
257             VnodeState pstate = ist.getPortState();
258             VInterface viface = new VInterface(getName(), state, pstate,
259                                                getVInterfaceConfig());
260             VInterfaceEvent.changed(mgr, path, viface, false);
261         }
262
263         return state;
264     }
265
266     /**
267      * Invoked when a node is added, removed, or changed.
268      *
269      * @param mgr     VTN Manager service.
270      * @param db      Virtual node state DB.
271      * @param prstate Current state of the parent node.
272      * @param ev      A {@link VtnNodeEvent} instance.
273      * @return  New state of the parent node.
274      */
275     final VnodeState notifyNode(VTNManagerImpl mgr,
276                                 ConcurrentMap<VTenantPath, Object> db,
277                                 VnodeState prstate, VtnNodeEvent ev) {
278         VnodeState state = notifyNode(mgr, db, ev);
279         return getParentState(state, prstate);
280     }
281
282     /**
283      * This method is called when some properties of a node connector are
284      * added/deleted/changed.
285      *
286      * @param mgr      VTN Manager service.
287      * @param db       Virtual node state DB.
288      * @param prstate  Current state of the parent node.
289      * @param ev       {@link VtnPortEvent} instance.
290      * @return  New state of this virtual node.
291      */
292     final VnodeState notifyNodeConnector(VTNManagerImpl mgr,
293                                          ConcurrentMap<VTenantPath, Object> db,
294                                          VnodeState prstate, VtnPortEvent ev) {
295         VnodeState state = notifyNodeConnector(mgr, db, ev);
296         return getParentState(state, prstate);
297     }
298
299     /**
300      * Determine whether the received packet should be treated as input of
301      * this virtual node or not.
302      *
303      * <p>
304      *   Note that this method does not see the state of this node.
305      *   This method returns {@code true} if the network specified by the
306      *   switch port and the VLAN ID is mapped to this node.
307      * </p>
308      *
309      * @param mgr   VTN Manager service.
310      * @param nc    A node connector where the packet was received.
311      * @param vlan  VLAN ID in the received packet.
312      * @return  {@code true} is returned only if the received packet should be
313      *          treated as input of this node.
314      */
315     final boolean match(VTNManagerImpl mgr, NodeConnector nc, short vlan) {
316         PortMapConfig pmconf = portMapConfig;
317         if (pmconf == null) {
318             return false;
319         }
320
321         VInterfaceState ist = getIfState(mgr);
322         NodeConnector mapped = ist.getMappedPort();
323         return (nc.equals(mapped) && pmconf.getVlan() == vlan);
324     }
325
326     /**
327      * Transmit the specified packet to this node.
328      *
329      * @param mgr   VTN manager service.
330      * @param pctx  The context of the packet.
331      * @param sent  A set of {@link PortVlan} which indicates the network
332      *              already processed.
333      * @throws RedirectFlowException
334      *    The given packet was redirected by a flow filter.
335      */
336     final void transmit(VTNManagerImpl mgr, PacketContext pctx,
337                         Set<PortVlan> sent) throws RedirectFlowException {
338         PortMapConfig pmconf = portMapConfig;
339         if (pmconf == null) {
340             return;
341         }
342
343         VInterfaceState ist = getIfState(mgr);
344         VnodeState state = ist.getState();
345         if (state != VnodeState.UP) {
346             return;
347         }
348
349         NodeConnector mapped = ist.getMappedPort();
350         if (mapped == null) {
351             return;
352         }
353         SalPort egress = SalPort.create(mapped);
354         if (egress == null) {
355             // This should never happen.
356             return;
357         }
358
359         short vlan = pmconf.getVlan();
360         PortVlan pvlan = new PortVlan(mapped, vlan);
361         if (!sent.add(pvlan)) {
362             // Already sent.
363             return;
364         }
365
366         Logger logger = getLogger();
367         PacketContext pc;
368         Ethernet frame;
369         try {
370             // Apply outgoing flow filters.
371             pc = outFlowFilters.evaluate(mgr, pctx, vlan);
372
373             // Create a new Ethernet frame to be transmitted.
374             frame = pc.createFrame(vlan);
375         } catch (DropFlowException e) {
376             // Filtered out by DROP filter.
377             return;
378         } catch (Exception e) {
379             mgr.logException(logger, getPath(), e);
380             return;
381         }
382
383         if (logger.isTraceEnabled()) {
384             VInterfacePath path = getInterfacePath();
385             logger.trace("{}:{}: Transmit packet to {} interface: {}",
386                          getContainerName(), path, path.getNodeType(),
387                          pc.getDescription(frame, egress, vlan));
388         }
389
390         mgr.transmit(egress, frame);
391     }
392
393     /**
394      * Return the flow filter instance configured in this virtual interface.
395      *
396      * <p>
397      *   This method must be called with holding the node lock.
398      * </p>
399      *
400      * @param out  {@code true} means that the outgoing flow filter.
401      *             {@code false} means that the incoming flow filter.
402      * @return  A {@link FlowFilterMap} instance.
403      */
404     final FlowFilterMap getFlowFilterMap(boolean out) {
405         return (out) ? outFlowFilters : inFlowFilters;
406     }
407
408     /**
409      * Flush cached network data associated with the network specified by
410      * a pair of switch port and VLAN ID.
411      *
412      * <p>
413      *   This method is used to flush network data cached by obsolete port
414      *   mapping.
415      * </p>
416      *
417      * @param mgr    VTN Manager service.
418      * @param port   A node connector associated with a switch port.
419      * @param vlan   A VLAN ID.
420      */
421     protected void flushCache(VTNManagerImpl mgr, NodeConnector port,
422                               short vlan) {
423         VNodePath npath = getPath();
424         if (port != null) {
425             // Remove flow entries relevant to obsolete port mapping.
426             VTNThreadData.removeFlows(mgr, npath.getTenantName(), port, vlan);
427         }
428
429         // Remove flow entries affected by this virtual node.
430         VTNThreadData.removeFlows(mgr, npath);
431     }
432
433     /**
434      * Redirect the packet to this virtual interface as outgoing packet.
435      *
436      * @param mgr     VTN Manager service.
437      * @param pctx    The context of the packet to be redirected.
438      * @param rex     An exception that keeps information about the packet
439      *                redirection.
440      * @param bridge  A {@link PortBridge} instance associated with this
441      *                virtual interface.
442      * @throws DropFlowException
443      *    The given packet was discarded by a DROP flow filter.
444      * @throws RedirectFlowException
445      *    The given packet was redirected by a REDIRECT flow filter.
446      * @throws VTNException  An error occurred.
447      */
448     protected void redirect(VTNManagerImpl mgr, PacketContext pctx,
449                             RedirectFlowException rex, PortBridge<?> bridge)
450         throws DropFlowException, RedirectFlowException, VTNException {
451         // Ensure that a physical switch port is mapped by port mapping.
452         PortMapConfig pmconf = portMapConfig;
453         NodeConnector mapped = getMappedPort(mgr);
454         if (pmconf == null || mapped == null) {
455             rex.notMapped(pctx);
456             throw new DropFlowException();
457         }
458
459         // Evaluate flow filters for outgoing packets.
460         // Note that this should never clone a PacketContext.
461         short vlan = pmconf.getVlan();
462         outFlowFilters.evaluate(mgr, pctx, vlan);
463
464         // Forward the packet.
465         bridge.forward(mgr, pctx, mapped, vlan);
466     }
467
468     /**
469      * Return a VLAN ID mapped to this virtual interface.
470      *
471      * @return  A VLAN ID mapped to this interface.
472      *          A negative value is returned if port mapping is not configured.
473      */
474     public short getVlan() {
475         PortMapConfig pmconf = portMapConfig;
476         return (pmconf == null) ? -1 : pmconf.getVlan();
477     }
478
479     /**
480      * Derive new node state from the specified node connector.
481      *
482      * <p>
483      *   Note that this method may update the state of the mapped switch port
484      *   in {@code ist}.
485      * </p>
486      *
487      * @param rdr  An {@link InventoryReader} instance.
488      * @param ist  Runtime state of this node.
489      * @return  New node state.
490      * @throws VTNException  An error occurred.
491      */
492     private VnodeState getNewState(InventoryReader rdr, VInterfaceState ist)
493         throws VTNException {
494         NodeConnector nc = ist.getMappedPort();
495         if (nc == null) {
496             return VnodeState.DOWN;
497         }
498
499         SalPort sport = SalPort.create(nc);
500         if (sport == null) {
501             // This should never happen.
502             return VnodeState.DOWN;
503         }
504
505         VtnPort vport = rdr.get(sport);
506         return getNewState(ist, vport);
507     }
508
509     /**
510      * Derive new node state from the specified node connector.
511      *
512      * <p>
513      *   Note that this method always updates the state of the mapped switch
514      *   port in {@code ist}.
515      * </p>
516      *
517      * @param ist     Runtime state of this node.
518      * @param vport   A {@link VtnPort} instance associated with the switch
519      *                port mapped to this node.
520      * @return  New node state.
521      */
522     private VnodeState getNewState(VInterfaceState ist, VtnPort vport) {
523         // Update the state of the mapped port.
524         VnodeState pstate = getPortState(vport);
525         ist.setPortState(pstate);
526
527         if (pstate == VnodeState.UP && InventoryUtils.isEdge(vport)) {
528             return VnodeState.UP;
529         }
530
531         return VnodeState.DOWN;
532     }
533
534     /**
535      * Get state of the switch port.
536      *
537      * @param reader  A {@link InventoryReader} instance.
538      * @param sport   A {@link SalPort} instance.
539      * @return  A {@link VnodeState} instance which represents the state of
540      *          the specified switc port.
541      * @throws VTNException  An error occurred.
542      */
543     private VnodeState getPortState(InventoryReader reader, SalPort sport)
544         throws VTNException {
545         if (sport == null) {
546             return VnodeState.UNKNOWN;
547         }
548
549         VtnPort vport = reader.get(sport);
550         return getPortState(vport);
551     }
552
553     /**
554      * Get state of the switch port.
555      *
556      * @param vport   A {@link VtnPort} instance.
557      * @return  A {@link VnodeState} instance which represents the state of
558      *          the specified switc port.
559      */
560     private VnodeState getPortState(VtnPort vport) {
561         if (vport == null) {
562             return VnodeState.UNKNOWN;
563         }
564
565         return (InventoryUtils.isEnabled(vport))
566             ? VnodeState.UP : VnodeState.DOWN;
567     }
568
569     /**
570      * Destroy mapping between the physical switch port and this node.
571      *
572      * @param mgr   VTN Manager service.
573      * @param db    Virtual node state DB.
574      * @param ist   Runtime state of this node.
575      * @return  New state of this virtual node.
576      *          {@code null} is returned if the port mapping configuration
577      *          was not changed.
578      */
579     private VnodeState unsetPortMap(VTNManagerImpl mgr,
580                                     ConcurrentMap<VTenantPath, Object> db,
581                                     VInterfaceState ist) {
582         PortMapConfig pmconf = portMapConfig;
583         if (pmconf == null) {
584             return null;
585         }
586
587         NodeConnector mapped = ist.getMappedPort();
588         if (mapped != null) {
589             unmapPort(mgr, db, ist, false, true);
590         }
591
592         portMapConfig = null;
593         PortMap pmap = new PortMap(pmconf, mapped);
594         VInterfacePath path = getInterfacePath();
595         PortMapEvent.removed(mgr, path, pmap, true);
596         setState(mgr, db, ist, VnodeState.UNKNOWN);
597
598         return VnodeState.UNKNOWN;
599     }
600
601     /**
602      * Determine whether the node connector satisfies the condition specified
603      * by the port map configuration.
604      *
605      * @param pmconf  Port mapping configuration.
606      * @param sport   A {@link SalPort} instance.
607      * @param vport   A {@link VtnPort} instance.
608      * @return  {@code true} is returned only if the given node connector
609      *          satisfies the condition.
610      */
611     private boolean portMatch(PortMapConfig pmconf, SalPort sport,
612                               VtnPort vport) {
613         Node pnode = pmconf.getNode();
614         SwitchPort port = pmconf.getPort();
615         String type = port.getType();
616         String id = port.getId();
617         NodeConnector nc = sport.getAdNodeConnector();
618         if (type != null && id != null) {
619             // This should never return null.
620             NodeConnector target =
621                 NodeConnector.fromStringNoNode(type, id, pnode);
622             if (!nc.equals(target)) {
623                 return false;
624             }
625         } else if (!pnode.equals(nc.getNode())) {
626             return false;
627         }
628
629         String name = port.getName();
630         return (name == null || name.equals(vport.getName()));
631     }
632
633     /**
634      * Establish port mapping.
635      *
636      * @param mgr       VTN Manager service.
637      * @param db        Virtual node state DB.
638      * @param ist       Runtime state of this node.
639      * @param sport     A {@link SalPort} to be mapped.
640      * @param resuming
641      *     {@code true} if this method is called by
642      *     {@link #resuming(VTNManagerImpl, ConcurrentMap, TxContext, VInterfaceState)}.
643      * @return  {@code true} is returned if the given port is successfully
644      *          mapped. {@code false} is returned if a switch port is already
645      *          mapped to this node.
646      */
647     private boolean mapPort(VTNManagerImpl mgr,
648                             ConcurrentMap<VTenantPath, Object> db,
649                             VInterfaceState ist, SalPort sport,
650                             boolean resuming) {
651         NodeConnector nc = sport.getAdNodeConnector();
652         short vlan = portMapConfig.getVlan();
653         PortVlan pvlan = new PortVlan(nc, vlan);
654         IVTNResourceManager resMgr = mgr.getResourceManager();
655         MapReference ref;
656         VInterfacePath path = getInterfacePath();
657
658         try {
659             ref = resMgr.registerPortMap(mgr, path, pvlan, null, !resuming);
660         } catch (VTNException e) {
661             Logger logger = getLogger();
662             logger.error(getContainerName() + ":" + path +
663                          ": Failed to map port to virtual interface: " +
664                          pvlan, e);
665             return false;
666         }
667
668         if (ref == null) {
669             ist.setMappedPort(nc);
670             db.put((VNodePath)path, ist);
671
672             // Clear dirty flag.
673             ist.isDirty();
674
675             if (!resuming) {
676                 PortMap pmap = new PortMap(portMapConfig, nc);
677                 PortMapEvent.changed(mgr, path, pmap, false);
678             }
679             return true;
680         }
681
682         Logger logger = getLogger();
683         logger.error("{}:{}: Switch port is already mapped to {}: {}",
684                      getContainerName(), path, ref.getAbsolutePath(), pvlan);
685         return false;
686     }
687
688     /**
689      * Unmap the physical switch port currently mapped.
690      *
691      * @param mgr    VTN Manager service.
692      * @param db     Virtual node state DB.
693      * @param ist    Runtime state of this node.
694      * @param event  If {@code true} is specified, a port map event is
695      *               notified.
696      * @param purge  If {@code true} is specified, network caches originated
697      *               by the port mapping will be purged.
698      */
699     private void unmapPort(VTNManagerImpl mgr,
700                            ConcurrentMap<VTenantPath, Object> db,
701                            VInterfaceState ist,
702                            boolean event, boolean purge) {
703         // Unregister port mapping.
704         short vlan = portMapConfig.getVlan();
705         PortVlan pvlan = new PortVlan(ist.getMappedPort(), vlan);
706         VInterfacePath path = getInterfacePath();
707         IVTNResourceManager resMgr = mgr.getResourceManager();
708         try {
709             resMgr.unregisterPortMap(mgr, path, pvlan, purge);
710         } catch (VTNException e) {
711             Logger logger = getLogger();
712             logger.error(getContainerName() + ":" + path +
713                          ": Failed to unmap port: " + pvlan, e);
714             // FALLTHROUGH
715         }
716
717         ist.setMappedPort(null);
718         db.put((VNodePath)path, ist);
719
720         // Clear dirty flag.
721         ist.isDirty();
722
723         if (event) {
724             PortMap pmap = new PortMap(portMapConfig, null);
725             PortMapEvent.changed(mgr, path, pmap, false);
726         }
727     }
728
729     /**
730      * Invoked when a node is added, removed, or changed.
731      *
732      * @param mgr  VTN Manager service.
733      * @param db   Virtual node state DB.
734      * @param ev   A {@link VtnNodeEvent} instance.
735      * @return  New state of this interface.
736      */
737     private VnodeState notifyNode(VTNManagerImpl mgr,
738                                   ConcurrentMap<VTenantPath, Object> db,
739                                   VtnNodeEvent ev) {
740         VInterfaceState ist = getIfState(db);
741         VnodeState cur = ist.getState();
742         if (ev.getUpdateType() != VtnUpdateType.REMOVED) {
743             return cur;
744         }
745
746         PortMapConfig pmconf = portMapConfig;
747         if (pmconf == null) {
748             return cur;
749         }
750
751         NodeConnector mapped = ist.getMappedPort();
752         if (mapped == null) {
753             return cur;
754         }
755
756         SalNode snode = ev.getSalNode();
757         Node pnode = pmconf.getNode();
758         if (pnode.equals(snode.getAdNode())) {
759             // No need to flush MAC address table entries and flow entries.
760             // It will be done by the VTN Manager service.
761             unmapPort(mgr, db, ist, true, false);
762             cur = VnodeState.DOWN;
763             setState(mgr, db, ist, cur);
764         }
765
766         return cur;
767     }
768
769     /**
770      * This method is called when some properties of a node connector are
771      * added/deleted/changed.
772      *
773      * @param mgr    VTN Manager service.
774      * @param db     Virtual node state DB.
775      * @param ev       {@link VtnPortEvent} instance.
776      * @return  New state of this virtual interface.
777      */
778     private VnodeState notifyNodeConnector(
779         VTNManagerImpl mgr, ConcurrentMap<VTenantPath, Object> db,
780         VtnPortEvent ev) {
781         VInterfaceState ist = getIfState(db);
782         VnodeState cur = ist.getState();
783         PortMapConfig pmconf = portMapConfig;
784         if (pmconf == null) {
785             return cur;
786         }
787
788         NodeConnector mapped = ist.getMappedPort();
789         VnodeState state = VnodeState.UNKNOWN;
790         SalPort sport = ev.getSalPort();
791         VtnPort vport = ev.getVtnPort();
792         switch (ev.getUpdateType()) {
793         case CREATED:
794             if (mapped != null || !portMatch(pmconf, sport, vport)) {
795                 return cur;
796             }
797
798             // Try to establish port mapping.
799             if (!mapPort(mgr, db, ist, sport, false)) {
800                 return cur;
801             }
802             state = getNewState(ist, vport);
803             break;
804
805         case CHANGED:
806             if (mapped == null) {
807                 // Check whether the port name was changed.
808                 // Map the port if its name matches the configuration.
809                 if (pmconf.getPort().getName() == null ||
810                     !portMatch(pmconf, sport, vport) ||
811                     !mapPort(mgr, db, ist, sport, false)) {
812                     return cur;
813                 }
814
815                 state = getNewState(ist, vport);
816             } else if (sport.getAdNodeConnector().equals(mapped)) {
817                 if (pmconf.getPort().getName() != null &&
818                     !portMatch(pmconf, sport, vport)) {
819                     // This port should be unmapped because its name has been
820                     // changed. In this case flow and MAC address table entries
821                     // need to be purged.
822                     unmapPort(mgr, db, ist, true, true);
823                     state = VnodeState.DOWN;
824                 } else {
825                     state = getNewState(ist, vport);
826                 }
827             } else {
828                 return cur;
829             }
830             break;
831
832         case REMOVED:
833             if (sport.getAdNodeConnector().equals(mapped)) {
834                 // No need to flush MAC address table entries and flow entries.
835                 // It will be done by the VTN Manager service.
836                 unmapPort(mgr, db, ist, true, false);
837                 state = VnodeState.DOWN;
838             } else {
839                 return cur;
840             }
841             break;
842
843         default:
844             return cur;
845         }
846
847         setState(mgr, db, ist, state);
848
849         return state;
850     }
851
852     /**
853      * Construct a flow match which specifies the given VLAN.
854      *
855      * @param vid  A VLAN ID.
856      * @return  A {@link VTNMatch} instance on success.
857      *          {@code null} on failure.
858      */
859     private VTNMatch createMatch(int vid) {
860         try {
861             VTNEtherMatch ematch =
862                 new VTNEtherMatch(null, null, null, vid, null);
863             return new VTNMatch(ematch, null, null);
864         } catch (RpcException e) {
865             // This should never happen.
866             getLogger().error("Failed to create VLAN match: " + vid, e);
867             return null;
868         }
869     }
870
871     /**
872      * Determine whether the given object is identical to this object.
873      *
874      * @param o  An object to be compared.
875      * @return   {@code true} if identical. Otherwise {@code false}.
876      */
877     @Override
878     public final boolean equals(Object o) {
879         if (o == this) {
880             return true;
881         }
882         if (!super.equals(o)) {
883             return false;
884         }
885
886         PortInterface pi = (PortInterface)o;
887         if (portMapConfig == null) {
888             return (pi.portMapConfig == null);
889         }
890
891         return portMapConfig.equals(pi.portMapConfig);
892     }
893
894     /**
895      * Return the hash code of this object.
896      *
897      * @return  The hash code.
898      */
899     @Override
900     public final int hashCode() {
901         int h = super.hashCode();
902         if (portMapConfig != null) {
903             h += (portMapConfig.hashCode() * 13);
904         }
905
906         return h;
907     }
908
909     // VirtualMapNode
910
911     /**
912      * Return path to this node.
913      *
914      * @return  Path to this node.
915      */
916     @Override
917     public VNodePath getPath() {
918         return (VNodePath)getInterfacePath();
919     }
920
921     /**
922      * Return path to the virtual mapping which maps the given host.
923      *
924      * @param mac   Unused.
925      * @param vlan  Unused.
926      * @return  Path to this interface.
927      */
928     @Override
929     public VNodePath getPath(long mac, short vlan) {
930         return getPath();
931     }
932
933     /**
934      * Return a {@link VNodeRoute} instance which indicates the packet was
935      * mapped by the port mapping.
936      *
937      * @param mac   Unused.
938      * @param vlan  Unused.
939      * @return  A {@link VNodeRoute} instance.
940      */
941     @Override
942     public final VNodeRoute getIngressRoute(long mac, short vlan) {
943         return new VNodeRoute((VNodePath)getInterfacePath(),
944                               VirtualRouteReason.PORTMAPPED);
945     }
946
947     /**
948      * Install a flow entry which drops every incoming packet.
949      *
950      * <p>
951      *   This method must be called with holding the node lock.
952      * </p>
953      *
954      * @param mgr   VTN Manager service.
955      * @param pctx  The context of the received packet.
956      */
957     @Override
958     public final void disableInput(VTNManagerImpl mgr, PacketContext pctx) {
959         PortMapConfig pmconf = portMapConfig;
960         if (pmconf == null) {
961             return;
962         }
963
964         VInterfaceState ist = getIfState(mgr);
965         SalPort mapped = SalPort.create(ist.getMappedPort());
966         if (mapped == null) {
967             return;
968         }
969
970         VTNMatch vmatch = createMatch((int)pmconf.getVlan());
971         if (vmatch == null) {
972             return;
973         }
974
975         VTNManagerProvider provider = pctx.getTxContext().getProvider();
976         VTNConfig vcfg = provider.getVTNConfig();
977         EtherAddress mac = vcfg.getControllerMacAddress();
978         int pri = vcfg.getL2FlowPriority();
979         int idle = pctx.getIdleTimeout();
980         int hard = pctx.getHardTimeout();
981         String tname = getTenantName();
982
983         VTNFlowBuilder builder =
984             new VTNFlowBuilder(tname, mac, vmatch, pri, idle, hard);
985         builder.addVirtualRoute(getIngressRoute(MacVlan.UNDEFINED, (short)0)).
986             setEgressVNodeRoute(null).
987             addDropFlow(mapped);
988         provider.addFlow(builder);
989     }
990
991     /**
992      * Evaluate flow filters for incoming packet configured in this virtual
993      * interface.
994      *
995      * @param mgr   VTN Manager service.
996      * @param pctx  The context of the received packet.
997      * @param vid   A VLAN ID to be used for packet matching.
998      *              A VLAN ID configured in the given packet is used if a
999      *              negative value is specified.
1000      * @throws DropFlowException
1001      *    The given packet was discarded by a flow filter.
1002      * @throws RedirectFlowException
1003      *    The given packet was redirected by a flow filter.
1004      */
1005     @Override
1006     public final void filterPacket(VTNManagerImpl mgr, PacketContext pctx,
1007                                    short vid)
1008         throws DropFlowException, RedirectFlowException {
1009         // Note that this call always returns `pctx'.
1010         inFlowFilters.evaluate(mgr, pctx, vid);
1011     }
1012
1013     /**
1014      * Evaluate flow filters for outgoing packet configured in this virtual
1015      * interface.
1016      *
1017      * @param mgr     VTN Manager service.
1018      * @param pctx    The context of the received packet.
1019      * @param vid     A VLAN ID to be used for packet matching.
1020      *                A VLAN ID configured in the given packet is used if a
1021      *                negative value is specified.
1022      * @param bridge  Never used.
1023      * @return  A {@link PacketContext} to be used for transmitting packet.
1024      * @throws DropFlowException
1025      *    The given packet was discarded by a flow filter.
1026      * @throws RedirectFlowException
1027      *    The given packet was redirected by a flow filter.
1028      */
1029     @Override
1030     public final PacketContext filterPacket(VTNManagerImpl mgr,
1031                                             PacketContext pctx, short vid,
1032                                             PortBridge<?> bridge)
1033         throws DropFlowException, RedirectFlowException {
1034         return outFlowFilters.evaluate(mgr, pctx, vid);
1035     }
1036
1037     // AbstractInterface
1038
1039     /**
1040      * Initialize virtual interface path for this instance.
1041      *
1042      * @param parent  The parent node that contains this interface.
1043      * @param name    The name of this interface.
1044      */
1045     @Override
1046     void setPath(AbstractBridge parent, String name) {
1047         super.setPath(parent, name);
1048
1049         // Initialize parent path for flow filter.
1050         inFlowFilters.setParent(this);
1051         outFlowFilters.setParent(this);
1052     }
1053
1054     /**
1055      * Change enable/disable configuration of this interface.
1056      *
1057      * <p>
1058      *   This method assumes that the call of this method is synchronized
1059      *   by the parent node.
1060      * </p>
1061      *
1062      * @param mgr      VTN Manager service.
1063      * @param ctx      MD-SAL datastore transaction context.
1064      * @param db       Virtual node state DB.
1065      * @param ist      Current runtime state of this interface.
1066      * @param enabled  {@code true} is passed if this interface has been
1067      *                 enabled.
1068      *                 {@code false} is passed if this interface has been
1069      *                 disabled.
1070      * @return  New state of this interface.
1071      */
1072     @Override
1073     protected final VnodeState changeEnableState(
1074         VTNManagerImpl mgr, TxContext ctx,
1075         ConcurrentMap<VTenantPath, Object> db, VInterfaceState ist,
1076         boolean enabled) {
1077         VnodeState state;
1078         if (enabled) {
1079             // Determine new state by the state of mapped port.
1080             if (portMapConfig == null) {
1081                 state = VnodeState.UNKNOWN;
1082             } else {
1083                 InventoryReader reader = ctx.getInventoryReader();
1084                 try {
1085                     state = getNewState(reader, ist);
1086                 } catch (Exception e) {
1087                     mgr.logException(getLogger(), getPath(), e);
1088                     state = VnodeState.UNKNOWN;
1089                 }
1090             }
1091         } else {
1092             // State of disabled interface must be DOWN.
1093             state = VnodeState.DOWN;
1094         }
1095
1096         ist.setState(state);
1097         if (ist.isDirty()) {
1098             PortMapConfig pmconf = portMapConfig;
1099             if (pmconf != null) {
1100                 // Invalidate all cached data added by the port mapping.
1101                 NodeConnector mapped = ist.getMappedPort();
1102                 short vlan = pmconf.getVlan();
1103                 flushCache(mgr, mapped, vlan);
1104             }
1105             db.put((VNodePath)getInterfacePath(), ist);
1106         }
1107
1108         return state;
1109     }
1110
1111     /**
1112      * Invoked when this interface is going to be resuming from the
1113      * configuration file.
1114      *
1115      * @param mgr  VTN Manager service.
1116      * @param db   Virtual node state DB.
1117      * @param ctx  A runtime context for MD-SAL datastore transaction task.
1118      * @param ist  Current state of this interface.
1119      * @return  New state of this interface.
1120      */
1121     @Override
1122     protected final VnodeState resuming(VTNManagerImpl mgr,
1123                                         ConcurrentMap<VTenantPath, Object> db,
1124                                         TxContext ctx, VInterfaceState ist) {
1125         PortMapConfig pmconf = portMapConfig;
1126         if (pmconf == null) {
1127             return VnodeState.UNKNOWN;
1128         }
1129
1130         NodeConnector mapped = ist.getMappedPort();
1131         if (mapped == null) {
1132             // Try to establish port mapping.
1133             Node node = pmconf.getNode();
1134             InventoryReader reader = ctx.getInventoryReader();
1135             SwitchPort port = pmconf.getPort();
1136             try {
1137                 SalPort sport = reader.findPort(node, port);
1138                 if (sport != null) {
1139                     mapPort(mgr, db, ist, sport, true);
1140                 }
1141
1142                 return getNewState(reader, ist);
1143             } catch (Exception e) {
1144                 mgr.logException(getLogger(), getPath(), e);
1145             }
1146         }
1147
1148         return VnodeState.UNKNOWN;
1149     }
1150
1151     /**
1152      * Invoked when this interface is going to be destroyed.
1153      *
1154      * @param mgr     VTN Manager service.
1155      * @param db      Virtual node state DB.
1156      * @param ist     Current state of this interface.
1157      * @param retain  {@code true} means that the parent node will be retained.
1158      *                {@code false} means that the parent node is being
1159      *                destroyed.
1160      */
1161     protected final void destroying(VTNManagerImpl mgr,
1162                                     ConcurrentMap<VTenantPath, Object> db,
1163                                     VInterfaceState ist, boolean retain) {
1164         if (retain && !(inFlowFilters.isEmpty() && outFlowFilters.isEmpty())) {
1165             // REVISIT: Select flow entries affected by obsolete flow filters.
1166             VTNThreadData.removeFlows(mgr, getTenantName());
1167         }
1168
1169         PortMapConfig pmconf = portMapConfig;
1170         if (pmconf != null) {
1171             // Destroy port mapping.
1172             NodeConnector mapped = ist.getMappedPort();
1173             PortMap pmap = new PortMap(pmconf, mapped);
1174             if (mapped != null) {
1175                 unmapPort(mgr, db, ist, false, retain);
1176             }
1177             PortMapEvent.removed(mgr, getInterfacePath(), pmap, false);
1178         }
1179     }
1180
1181     /**
1182      * Notify the listener of current configuration.
1183      *
1184      * @param mgr       VTN Manager service.
1185      * @param listener  VTN manager listener service.
1186      */
1187     @Override
1188     final void notifyConfiguration(VTNManagerImpl mgr,
1189                                    IVTNManagerAware listener) {
1190         super.notifyConfiguration(mgr, listener);
1191
1192         PortMapConfig pmconf = portMapConfig;
1193         if (pmconf != null) {
1194             VInterfaceState ist = getIfState(mgr);
1195             NodeConnector mapped = ist.getMappedPort();
1196             PortMap pmap = new PortMap(pmconf, mapped);
1197             mgr.notifyChange(listener, getInterfacePath(), pmap,
1198                              UpdateType.ADDED);
1199         }
1200     }
1201 }