Use documenting constants for put()
[netvirt.git] / vpnservice / elanmanager / elanmanager-impl / src / main / java / org / opendaylight / netvirt / elan / internal / ElanPacketInHandler.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  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 package org.opendaylight.netvirt.elan.internal;
9
10 import com.google.common.base.Optional;
11
12 import java.math.BigInteger;
13 import java.util.Collections;
14 import org.opendaylight.controller.liblldp.NetUtils;
15 import org.opendaylight.controller.liblldp.PacketException;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
20 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
21 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
22 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
23 import org.opendaylight.genius.mdsalutil.NWUtil;
24 import org.opendaylight.genius.mdsalutil.NwConstants;
25 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
26 import org.opendaylight.netvirt.elan.evpn.utils.EvpnUtils;
27 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
28 import org.opendaylight.netvirt.elan.utils.ElanUtils;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._if.indexes._interface.map.IfIndexInterface;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.tag.name.map.ElanTagName;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntry;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.NoMatch;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 @SuppressWarnings("deprecation")
46 public class ElanPacketInHandler implements PacketProcessingListener {
47
48     private static final Logger LOG = LoggerFactory.getLogger(ElanPacketInHandler.class);
49
50     private final DataBroker broker;
51     private final IInterfaceManager interfaceManager;
52     private final ElanUtils elanUtils;
53     private final ElanL2GatewayUtils elanL2GatewayUtils;
54     private final EvpnUtils evpnUtils;
55
56     public ElanPacketInHandler(DataBroker dataBroker, final IInterfaceManager interfaceManager, ElanUtils elanUtils,
57                                EvpnUtils evpnUtils) {
58         broker = dataBroker;
59         this.interfaceManager = interfaceManager;
60         this.elanUtils = elanUtils;
61         this.elanL2GatewayUtils = elanUtils.getElanL2GatewayUtils();
62         this.evpnUtils = evpnUtils;
63     }
64
65     @Override
66     public void onPacketReceived(PacketReceived notification) {
67         Class<? extends PacketInReason> pktInReason = notification.getPacketInReason();
68         short tableId = notification.getTableId().getValue();
69         if (pktInReason == NoMatch.class && tableId == NwConstants.ELAN_SMAC_TABLE) {
70             ElanManagerCounters.unknown_smac_pktin_rcv.inc();
71             try {
72                 byte[] data = notification.getPayload();
73                 Ethernet res = new Ethernet();
74
75                 res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
76
77                 byte[] srcMac = res.getSourceMACAddress();
78                 final String macAddress = NWUtil.toStringMacAddress(srcMac);
79                 final BigInteger metadata = notification.getMatch().getMetadata().getMetadata();
80                 final long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
81
82                 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
83
84                 Optional<IfIndexInterface> interfaceInfoOp = elanUtils.getInterfaceInfoByInterfaceTag(portTag);
85                 if (!interfaceInfoOp.isPresent()) {
86                     LOG.warn("There is no interface for given portTag {}", portTag);
87                     return;
88                 }
89                 String interfaceName = interfaceInfoOp.get().getInterfaceName();
90                 LOG.debug("Received a packet with srcMac: {} ElanTag: {} PortTag: {} InterfaceName: {}", macAddress,
91                         elanTag, portTag, interfaceName);
92                 ElanTagName elanTagName = elanUtils.getElanInfoByElanTag(elanTag);
93                 if (elanTagName == null) {
94                     LOG.warn("not able to find elanTagName in elan-tag-name-map for elan tag {}", elanTag);
95                     return;
96                 }
97                 String elanName = elanTagName.getName();
98                 PhysAddress physAddress = new PhysAddress(macAddress);
99                 MacEntry oldMacEntry = elanUtils.getMacEntryForElanInstance(elanName, physAddress).orNull();
100                 boolean isVlanOrFlatProviderIface = interfaceManager.isExternalInterface(interfaceName);
101
102                 Optional<IpAddress> srcIpAddress = elanUtils.getSourceIpAddress(res);
103                 MacEntry newMacEntry = null;
104                 BigInteger timeStamp = new BigInteger(String.valueOf(System.currentTimeMillis()));
105                 if (!srcIpAddress.isPresent()) {
106                     newMacEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
107                             .setKey(new MacEntryKey(physAddress))
108                             .setControllerLearnedForwardingEntryTimestamp(timeStamp)
109                             .setIsStaticAddress(false).build();
110                 } else {
111                     newMacEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
112                             .setIpPrefix(srcIpAddress.get()).setKey(new MacEntryKey(physAddress))
113                             .setControllerLearnedForwardingEntryTimestamp(timeStamp)
114                             .setIsStaticAddress(false).build();
115                 }
116                 if (srcIpAddress.isPresent()) {
117                     String prefix = srcIpAddress.get().getIpv4Address().getValue();
118                     InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
119                     ElanInstance elanInstance = elanUtils.getElanInstanceByName(broker, elanName);
120                     evpnUtils.advertisePrefix(elanInstance, macAddress, prefix, interfaceName, interfaceInfo.getDpId());
121                 }
122                 final DataStoreJobCoordinator portDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
123                 enqueueJobForMacSpecificTasks(macAddress, elanTag, interfaceName, elanName, physAddress, oldMacEntry,
124                         newMacEntry, isVlanOrFlatProviderIface, portDataStoreCoordinator);
125
126                 ElanInstance elanInstance = ElanUtils.getElanInstanceByName(broker, elanName);
127                 InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
128                 if (interfaceInfo == null) {
129                     LOG.trace("Interface:{} is not present under Config DS", interfaceName);
130                     return;
131                 }
132                 enqueueJobForDPNSpecificTasks(macAddress, elanTag, interfaceName, physAddress, elanInstance,
133                         interfaceInfo, oldMacEntry, newMacEntry, isVlanOrFlatProviderIface, portDataStoreCoordinator);
134
135
136             } catch (PacketException e) {
137                 LOG.error("Failed to decode packet: {}", notification, e);
138             }
139         }
140     }
141
142     private void enqueueJobForMacSpecificTasks(final String macAddress, final long elanTag, String interfaceName,
143                                                String elanName, PhysAddress physAddress,
144                                                MacEntry oldMacEntry, MacEntry newMacEntry,
145                                                final boolean isVlanOrFlatProviderIface,
146                                                final DataStoreJobCoordinator portDataStoreCoordinator) {
147         portDataStoreCoordinator.enqueueJob(ElanUtils.getElanMacKey(elanTag, macAddress), () -> {
148             WriteTransaction writeTx = broker.newWriteOnlyTransaction();
149             if (oldMacEntry != null && oldMacEntry.getInterface().equals(interfaceName)) {
150                 // This should never occur because of ovs temporary mac learning
151                 ElanManagerCounters.unknown_smac_pktin_forwarding_entries_removed.inc();
152             } else if (oldMacEntry != null && !isVlanOrFlatProviderIface) {
153                 long macTimeStamp = oldMacEntry.getControllerLearnedForwardingEntryTimestamp().longValue();
154                 if (System.currentTimeMillis() > macTimeStamp + 1000) {
155                     InstanceIdentifier<MacEntry> macEntryId = ElanUtils
156                             .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName,
157                                     physAddress);
158                     writeTx.delete(LogicalDatastoreType.OPERATIONAL, macEntryId);
159                 } else {
160                     // New FEs flood their packets on all interfaces. This
161                     // can lead
162                     // to many contradicting packet_ins. Ignore all packets
163                     // received
164                     // within 1s after the first packet_in
165                     ElanManagerCounters.unknown_smac_pktin_mac_migration_ignored_due_to_protection.inc();
166                 }
167             } else if (oldMacEntry != null) {
168                 ElanManagerCounters.unknown_smac_pktin_removed_for_relearned.inc();
169             }
170             // This check is required only to update elan-forwarding-tables when mac is learned
171             // in ports (example: VM interfaces) other than on vlan provider port.
172             if (!isVlanOrFlatProviderIface && oldMacEntry == null) {
173                 InstanceIdentifier<MacEntry> elanMacEntryId =
174                         ElanUtils.getMacEntryOperationalDataPath(elanName, physAddress);
175                 writeTx.put(LogicalDatastoreType.OPERATIONAL, elanMacEntryId, newMacEntry,
176                         WriteTransaction.CREATE_MISSING_PARENTS);
177             }
178             return Collections.singletonList(writeTx.submit());
179         });
180     }
181
182     private void enqueueJobForDPNSpecificTasks(final String macAddress, final long elanTag, String interfaceName,
183                                                PhysAddress physAddress, ElanInstance elanInstance,
184                                                InterfaceInfo interfaceInfo, MacEntry oldMacEntry,
185                                                MacEntry newMacEntry, boolean isVlanOrFlatProviderIface,
186                                                final DataStoreJobCoordinator portDataStoreCoordinator) {
187         portDataStoreCoordinator
188                 .enqueueJob(ElanUtils.getElanMacDPNKey(elanTag, macAddress, interfaceInfo.getDpId()), () -> {
189                     macMigrationFlowsCleanup(interfaceName, elanInstance, oldMacEntry, isVlanOrFlatProviderIface);
190                     BigInteger dpId = interfaceManager.getDpnForInterface(interfaceName);
191                     elanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId,
192                             Collections.singletonList(physAddress));
193                     ElanManagerCounters.unknown_smac_pktin_learned.inc();
194                     WriteTransaction flowWritetx = broker.newWriteOnlyTransaction();
195                     elanUtils.setupMacFlows(elanInstance, interfaceInfo, elanInstance.getMacTimeout(),
196                             macAddress, !isVlanOrFlatProviderIface, flowWritetx);
197                     InstanceIdentifier<MacEntry> macEntryId =
198                             ElanUtils.getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
199                     flowWritetx.put(LogicalDatastoreType.OPERATIONAL, macEntryId, newMacEntry,
200                             WriteTransaction.CREATE_MISSING_PARENTS);
201                     return Collections.singletonList(flowWritetx.submit());
202                 });
203     }
204
205     private void macMigrationFlowsCleanup(String interfaceName, ElanInstance elanInstance, MacEntry macEntry,
206                                           boolean isVlanOrFlatProviderIface) {
207         if (macEntry != null && !macEntry.getInterface().equals(interfaceName)
208                 && !isVlanOrFlatProviderIface) {
209             tryAndRemoveInvalidMacEntry(elanInstance.getElanInstanceName(), macEntry);
210             ElanManagerCounters.unknown_smac_pktin_flows_removed_for_relearned.inc();
211         }
212     }
213
214     /*
215      * Though this method is a little costlier because it uses try-catch
216      * construct, it is used only in rare scenarios like MAC movement or invalid
217      * Static MAC having been added on a wrong ELAN.
218      */
219     private void tryAndRemoveInvalidMacEntry(String elanName, MacEntry macEntry) {
220         ElanInstance elanInfo = ElanUtils.getElanInstanceByName(broker, elanName);
221         if (elanInfo == null) {
222             LOG.warn("MAC {} is been added (either statically or dynamically) for an invalid Elan {}. "
223                     + "Manual cleanup may be necessary", macEntry.getMacAddress(), elanName);
224             return;
225         }
226
227         InterfaceInfo oldInterfaceLport = interfaceManager.getInterfaceInfo(macEntry.getInterface());
228         if (oldInterfaceLport == null) {
229             LOG.warn("MAC {} is been added (either statically or dynamically) on an invalid Logical Port {}. "
230                             + "Manual cleanup may be necessary",
231                     macEntry.getMacAddress(), macEntry.getInterface());
232             return;
233         }
234         WriteTransaction flowDeletetx = broker.newWriteOnlyTransaction();
235         elanUtils.deleteMacFlows(elanInfo, oldInterfaceLport, macEntry, flowDeletetx);
236         flowDeletetx.submit();
237         elanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo,
238                 Collections.singletonList(macEntry.getMacAddress()));
239     }
240
241 }