2 * Copyright (c) 2015 - 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.vpnservice.fibmanager;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
15 import java.math.BigInteger;
16 import java.net.InetAddress;
17 import java.net.UnknownHostException;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.Future;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.vpnmanager.api.IVpnManager;
31 import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener;
32 import org.opendaylight.vpnservice.itm.globals.ITMConstants;
33 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
34 import org.opendaylight.vpnservice.mdsalutil.ActionType;
35 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
36 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
37 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
38 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
39 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
40 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
41 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
42 import org.opendaylight.vpnservice.mdsalutil.NwConstants;
43 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.OpState;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.PrefixToInterface;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnToExtraroute;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.VpnIds;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.VpnIdsKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.Vpn;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.VpnKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.vpn.Extraroute;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.vpn.ExtrarouteBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.vpn.ExtrarouteKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTablesKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.vrfentries.VrfEntry;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeBase;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeMplsOverGre;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetTunnelTypeInputBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetTunnelTypeOutput;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
74 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
77 import org.opendaylight.yangtools.concepts.ListenerRegistration;
78 import org.opendaylight.yangtools.yang.binding.DataObject;
79 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
80 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
81 import org.opendaylight.yangtools.yang.common.RpcResult;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
85 public class FibManager extends AbstractDataChangeListener<VrfEntry> implements AutoCloseable{
86 private static final Logger LOG = LoggerFactory.getLogger(FibManager.class);
87 private static final String FLOWID_PREFIX = "L3.";
88 private ListenerRegistration<DataChangeListener> listenerRegistration;
89 private final DataBroker broker;
90 private IMdsalApiManager mdsalManager;
91 private IVpnManager vpnmanager;
92 private NexthopManager nextHopManager;
93 private ItmRpcService itmManager;
94 private OdlInterfaceRpcService interfaceManager;
95 private static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
96 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
97 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
98 private static final BigInteger METADATA_MASK_CLEAR = new BigInteger("000000FFFFFFFFFF", 16);
99 private static final BigInteger CLEAR_METADATA = BigInteger.valueOf(0);
100 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
103 public FibManager(final DataBroker db) {
104 super(VrfEntry.class);
106 registerListener(db);
110 public void close() throws Exception {
111 if (listenerRegistration != null) {
113 listenerRegistration.close();
114 } catch (final Exception e) {
115 LOG.error("Error when cleaning up DataChangeListener.", e);
117 listenerRegistration = null;
119 LOG.info("Fib Manager Closed");
122 public void setNextHopManager(NexthopManager nextHopManager) {
123 this.nextHopManager = nextHopManager;
126 public void setMdsalManager(IMdsalApiManager mdsalManager) {
127 this.mdsalManager = mdsalManager;
130 public void setVpnmanager(IVpnManager vpnmanager) {
131 this.vpnmanager = vpnmanager;
134 public void setITMRpcService(ItmRpcService itmManager) {
135 this.itmManager = itmManager;
138 public void setInterfaceManager(OdlInterfaceRpcService ifManager) {
139 this.interfaceManager = ifManager;
142 private void registerListener(final DataBroker db) {
144 listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
145 getWildCardPath(), FibManager.this, DataChangeScope.SUBTREE);
146 } catch (final Exception e) {
147 LOG.error("FibManager DataChange listener registration fail!", e);
148 throw new IllegalStateException("FibManager registration Listener failed.", e);
153 private InstanceIdentifier<VrfEntry> getWildCardPath() {
154 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
159 protected void add(final InstanceIdentifier<VrfEntry> identifier,
160 final VrfEntry vrfEntry) {
161 LOG.trace("key: " + identifier + ", value=" + vrfEntry );
162 createFibEntries(identifier, vrfEntry);
166 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
167 LOG.trace("key: " + identifier + ", value=" + vrfEntry);
168 deleteFibEntries(identifier, vrfEntry);
172 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
173 LOG.trace("key: " + identifier + ", original=" + original + ", update=" + update );
174 createFibEntries(identifier, update);
177 private void createFibEntries(final InstanceIdentifier<VrfEntry> identifier,
178 final VrfEntry vrfEntry) {
179 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
180 Preconditions.checkNotNull(vrfTableKey, "VrfTablesKey cannot be null or empty!");
181 Preconditions.checkNotNull(vrfEntry, "VrfEntry cannot be null or empty!");
183 VpnInstanceOpDataEntry vpnInstance = getVpnInstance(vrfTableKey.getRouteDistinguisher());
184 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available!");
185 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId() + "has null vpnId!");
187 Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
188 BigInteger localDpnId = createLocalFibEntry(vpnInstance.getVpnId(),
189 vrfTableKey.getRouteDistinguisher(), vrfEntry);
190 if (vpnToDpnList != null) {
191 for (VpnToDpnList curDpn : vpnToDpnList) {
192 if (!curDpn.getDpnId().equals(localDpnId)) {
193 createRemoteFibEntry(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(),
194 vrfTableKey, vrfEntry);
200 public BigInteger createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
201 BigInteger localDpnId = BigInteger.ZERO;
202 Prefixes localNextHopInfo = getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
203 String localNextHopIP = vrfEntry.getDestPrefix();
205 if(localNextHopInfo == null) {
206 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
207 Extraroute extra_route = getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
208 if (extra_route != null) {
209 localNextHopInfo = getPrefixToInterface(vpnId, extra_route.getNexthopIp() + "/32");
210 localNextHopIP = extra_route.getNexthopIp() + "/32";
214 if(localNextHopInfo != null) {
215 localDpnId = localNextHopInfo.getDpnId();
216 long groupId = nextHopManager.createLocalNextHop(vpnId, localDpnId, localNextHopInfo.getVpnInterfaceName(), localNextHopIP);
217 List<ActionInfo> actionInfos = new ArrayList<ActionInfo>();
219 actionInfos.add(new ActionInfo(ActionType.group, new String[] { String.valueOf(groupId)}));
221 makeConnectedRoute(localDpnId, vpnId, vrfEntry, rd, actionInfos, NwConstants.ADD_FLOW);
222 makeLFibTableEntry(localDpnId, vrfEntry.getLabel(), groupId, vrfEntry.getNextHopAddress(), NwConstants.ADD_FLOW);
224 LOG.debug("Installing tunnel table entry on dpn {} for interface {} with label {}",
225 localDpnId, localNextHopInfo.getVpnInterfaceName(), vrfEntry.getLabel());
226 makeTunnelTableEntry(localDpnId, vrfEntry.getLabel(), groupId);
232 private void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/) {
233 List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
234 actionsInfos.add(new ActionInfo(ActionType.group, new String[] { String.valueOf(groupId) }));
237 createTerminatingServiceActions(dpId, (int)label, actionsInfos);
239 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully {}",
240 dpId, label, groupId);
243 public void createTerminatingServiceActions( BigInteger destDpId, int label, List<ActionInfo> actionsInfos) {
244 List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
246 LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", destDpId , label,actionsInfos);
249 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
250 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(label)}));
252 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
253 mkInstructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos));
255 FlowEntity terminatingServiceTableFlowEntity = MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
256 getFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,label), 5, String.format("%s:%d","TST Flow Entry ",label),
257 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)),mkMatches, mkInstructions);
259 mdsalManager.installFlow(terminatingServiceTableFlowEntity);
262 private void removeTunnelTableEntry(BigInteger dpId, long label) {
263 FlowEntity flowEntity;
264 LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId , label);
265 List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
267 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(label)}));
268 flowEntity = MDSALUtil.buildFlowEntity(dpId,
269 NwConstants.INTERNAL_TUNNEL_TABLE,
270 getFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int)label),
271 5, String.format("%s:%d","TST Flow Entry ",label), 0, 0,
272 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
273 mdsalManager.removeFlow(flowEntity);
274 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpId, label);
277 public BigInteger deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
278 BigInteger localDpnId = BigInteger.ZERO;
279 boolean isExtraRoute = false;
280 VpnNexthop localNextHopInfo = nextHopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
281 String localNextHopIP = vrfEntry.getDestPrefix();
283 if(localNextHopInfo == null) {
284 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
285 Extraroute extra_route = getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
286 if (extra_route != null) {
287 localNextHopInfo = nextHopManager.getVpnNexthop(vpnId, extra_route.getNexthopIp());
288 localNextHopIP = extra_route.getNexthopIp() + "/32";
294 if(localNextHopInfo != null) {
295 localDpnId = localNextHopInfo.getDpnId();
296 Prefixes prefix = getPrefixToInterface(vpnId, isExtraRoute ? localNextHopIP : vrfEntry.getDestPrefix());
297 Optional<OpState> opStateData = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
298 FibUtil.getVpnInterfaceOpStateIdentifier(prefix.getVpnInterfaceName()));
299 if(opStateData.isPresent() && !opStateData.get().isStateUp())
301 makeConnectedRoute(localDpnId, vpnId, vrfEntry, rd, null /* invalid */,
302 NwConstants.DEL_FLOW);
303 makeLFibTableEntry(localDpnId, vrfEntry.getLabel(), 0 /* invalid */,
304 vrfEntry.getNextHopAddress(), NwConstants.DEL_FLOW);
305 removeTunnelTableEntry(localDpnId, vrfEntry.getLabel());
306 deleteLocalAdjacency(localDpnId, vpnId, localNextHopIP);
312 private InstanceIdentifier<Prefixes> getPrefixToInterfaceIdentifier(Long vpnId, String ipPrefix) {
313 return InstanceIdentifier.builder(PrefixToInterface.class)
314 .child(VpnIds.class, new VpnIdsKey(vpnId)).child(Prefixes.class, new PrefixesKey(ipPrefix)).build();
317 private Prefixes getPrefixToInterface(Long vpnId, String ipPrefix) {
318 Optional<Prefixes> localNextHopInfoData =
319 FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, getPrefixToInterfaceIdentifier(vpnId, ipPrefix));
320 return localNextHopInfoData.isPresent() ? localNextHopInfoData.get() : null;
323 private InstanceIdentifier<Extraroute> getVpnToExtrarouteIdentifier(String vrfId, String ipPrefix) {
324 return InstanceIdentifier.builder(VpnToExtraroute.class)
325 .child(Vpn.class, new VpnKey(vrfId)).child(Extraroute.class,
326 new ExtrarouteKey(ipPrefix)).build();
329 private Extraroute getVpnToExtraroute(String rd, String ipPrefix) {
330 Optional<Extraroute> extraRouteInfo =
331 FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, getVpnToExtrarouteIdentifier(rd, ipPrefix));
332 return extraRouteInfo.isPresent() ? extraRouteInfo.get() : null;
336 private Class<? extends TunnelTypeBase> getTunnelType(String ifName) {
338 Future<RpcResult<GetTunnelTypeOutput>> result = interfaceManager.getTunnelType(
339 new GetTunnelTypeInputBuilder().setIntfName(ifName).build());
340 RpcResult<GetTunnelTypeOutput> rpcResult = result.get();
341 if(!rpcResult.isSuccessful()) {
342 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
344 return rpcResult.getResult().getTunnelType();
347 } catch (InterruptedException | ExecutionException e) {
348 LOG.warn("Exception when getting tunnel interface Id for tunnel type {}", e);
354 private void createRemoteFibEntry(final BigInteger localDpnId, final BigInteger remoteDpnId,
355 final long vpnId, final VrfTablesKey vrfTableKey,
356 final VrfEntry vrfEntry) {
357 String rd = vrfTableKey.getRouteDistinguisher();
358 LOG.debug("adding route " + vrfEntry.getDestPrefix() + " " + rd);
359 /********************************************/
360 String tunnelInterface = resolveAdjacency(localDpnId, remoteDpnId, vpnId, vrfEntry, rd);
361 if(tunnelInterface == null) {
362 LOG.error("Could not get interface for nexthop: {} in vpn {}",
363 vrfEntry.getNextHopAddress(), rd);
364 LOG.warn("Failed to add Route: {} in vpn: {}",
365 vrfEntry.getDestPrefix(), rd);
368 List<ActionInfo> actionInfos = new ArrayList<>();
369 Class<? extends TunnelTypeBase> tunnel_type = getTunnelType(tunnelInterface);
370 if (tunnel_type.equals(TunnelTypeMplsOverGre.class)) {
371 LOG.debug("Push label action for prefix {}", vrfEntry.getDestPrefix());
372 actionInfos.add(new ActionInfo(ActionType.push_mpls, new String[] { null }));
373 actionInfos.add(new ActionInfo(ActionType.set_field_mpls_label, new String[] { Long.toString(vrfEntry.getLabel())}));
375 int label = vrfEntry.getLabel().intValue();
377 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
378 if(tunnel_type.equals(TunnelTypeVxlan.class)) {
379 tunnelId = BigInteger.valueOf(label);
381 tunnelId = BigInteger.valueOf(label);
383 LOG.debug("adding set tunnel id action for label {}", label);
384 actionInfos.add(new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[]{
387 actionInfos.addAll(nextHopManager.getEgressActionsForInterface(tunnelInterface));
389 List<ActionInfo> actionInfos = resolveAdjacency(localDpnId, remoteDpnId, vpnId, vrfEntry);
390 if(actionInfos == null) {
391 LOG.error("Could not get nexthop group id for nexthop: {} in vpn {}",
392 vrfEntry.getNextHopAddress(), rd);
393 LOG.warn("Failed to add Route: {} in vpn: {}",
394 vrfEntry.getDestPrefix(), rd);
397 BigInteger dpnId = nextHopManager.getDpnForPrefix(vpnId, vrfEntry.getDestPrefix());
399 //This route may be extra route... try to query with nexthop Ip
400 LOG.debug("Checking for extra route to install remote fib entry {}", vrfEntry.getDestPrefix());
401 dpnId = nextHopManager.getDpnForPrefix(vpnId, vrfEntry.getNextHopAddress() + "/32");
404 LOG.debug("Push label action for prefix {}", vrfEntry.getDestPrefix());
405 actionInfos.add(new ActionInfo(ActionType.push_mpls, new String[] { null }));
406 actionInfos.add(new ActionInfo(ActionType.set_field_mpls_label, new String[] { Long.toString(vrfEntry.getLabel())}));
408 int label = vrfEntry.getLabel().intValue();
409 LOG.debug("adding set tunnel id action for label {}", label);
410 actionInfos.add(new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
411 MetaDataUtil.getTunnelIdWithValidVniBitAndVniSet(label),
412 MetaDataUtil.METADA_MASK_VALID_TUNNEL_ID_BIT_AND_TUNNEL_ID }));
415 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, actionInfos, NwConstants.ADD_FLOW);
417 "Successfully added fib entry for " + vrfEntry.getDestPrefix() + " vpnId " + vpnId);
420 private void delIntfFromDpnToVpnList(long vpnId, BigInteger dpnId, String intfName, String rd) {
421 InstanceIdentifier<VpnToDpnList> id = FibUtil.getVpnToDpnListIdentifier(rd, dpnId);
422 Optional<VpnToDpnList> dpnInVpn = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
423 if (dpnInVpn.isPresent()) {
424 List<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
425 .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> vpnInterfaces = dpnInVpn.get().getVpnInterfaces();
426 org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces
427 currVpnInterface = new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesBuilder().setInterfaceName(intfName).build();
429 if (vpnInterfaces.remove(currVpnInterface)) {
430 if (vpnInterfaces.isEmpty()) {
431 //FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL, id);
432 LOG.trace("cleanUpOpDataForFib: cleanUpDpnForVpn: {}, {}", dpnId, vpnId);
433 cleanUpDpnForVpn(dpnId, vpnId, rd);
435 LOG.trace("cleanUpOpDataForFib: delete interface: {} on {}", intfName, dpnId);
436 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL, id.child(
437 org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
438 .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces.class,
439 new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesKey(intfName)));
445 private void cleanUpOpDataForFib(Long vpnId, String rd, final VrfEntry vrfEntry) {
446 /* Get interface info from prefix to interface mapping;
447 Use the interface info to get the corresponding vpn interface op DS entry,
448 remove the adjacency corresponding to this fib entry.
449 If adjacency removed is the last adjacency, clean up the following:
450 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
451 - prefix to interface entry
452 - vpn interface op DS
454 Prefixes prefixInfo = getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
455 boolean extra_route = false;
456 if (prefixInfo == null) {
457 prefixInfo = getPrefixToInterface(vpnId, vrfEntry.getNextHopAddress() + "/32");
460 if (prefixInfo == null)
461 return; //Don't have any info for this prefix (shouldn't happen); need to return
462 String ifName = prefixInfo.getVpnInterfaceName();
463 Optional<Adjacencies> optAdjacencies = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, FibUtil.getAdjListPath(ifName));
465 if (optAdjacencies.isPresent()) {
466 numAdj = optAdjacencies.get().getAdjacency().size();
468 LOG.trace("cleanUpOpDataForFib: remove adjacency for prefix: {} {}", vpnId, vrfEntry.getDestPrefix());
469 //remove adjacency corr to prefix
470 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL, FibUtil.getAdjacencyIdentifier(ifName, vrfEntry.getDestPrefix()));
472 if((numAdj - 1) == 0) { //there are no adjacencies left for this vpn interface, clean up
473 //clean up the vpn interface from DpnToVpn list
474 delIntfFromDpnToVpnList(vpnId, prefixInfo.getDpnId(), ifName, rd);
475 LOG.trace("cleanUpOpDataForFib: Delete prefix to interface and vpnInterface ");
476 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
477 FibUtil.getPrefixToInterfaceIdentifier(
479 (extra_route) ? vrfEntry.getNextHopAddress() + "/32" : vrfEntry.getDestPrefix()));
480 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
481 FibUtil.getVpnInterfaceIdentifier(ifName));
485 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier,
486 final VrfEntry vrfEntry) {
487 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
488 Preconditions.checkNotNull(vrfTableKey, "VrfTablesKey cannot be null or empty!");
489 Preconditions.checkNotNull(vrfEntry, "VrfEntry cannot be null or empty!");
491 VpnInstanceOpDataEntry vpnInstance = getVpnInstance(vrfTableKey.getRouteDistinguisher());
492 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available!");
493 Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
494 BigInteger localDpnId = deleteLocalFibEntry(vpnInstance.getVpnId(),
495 vrfTableKey.getRouteDistinguisher(), vrfEntry);
496 if (vpnToDpnList != null) {
497 for (VpnToDpnList curDpn : vpnToDpnList) {
498 if (!curDpn.getDpnId().equals(localDpnId)) {
499 deleteRemoteRoute(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey, vrfEntry);
503 //The flow/group entry has been deleted from config DS; need to clean up associated operational
504 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
505 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
508 public void deleteRemoteRoute(final BigInteger localDpnId, final BigInteger remoteDpnId,
509 final long vpnId, final VrfTablesKey vrfTableKey,
510 final VrfEntry vrfEntry) {
511 LOG.debug("deleting route "+ vrfEntry.getDestPrefix() + " "+vpnId);
512 String rd = vrfTableKey.getRouteDistinguisher();
513 String egressInterface = resolveAdjacency(localDpnId, remoteDpnId, vpnId, vrfEntry, rd);
514 if(egressInterface == null) {
515 LOG.error("Could not get nexthop group id for nexthop: {} in vpn {}",
516 vrfEntry.getNextHopAddress(), rd);
517 LOG.warn("Failed to delete Route: {} in vpn: {}",
518 vrfEntry.getDestPrefix(), rd);
522 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW);
523 LOG.debug("Successfully delete fib entry for "+ vrfEntry.getDestPrefix() + " vpnId "+vpnId);
526 private long getIpAddress(byte[] rawIpAddress) {
527 return (((rawIpAddress[0] & 0xFF) << (3 * 8)) + ((rawIpAddress[1] & 0xFF) << (2 * 8))
528 + ((rawIpAddress[2] & 0xFF) << (1 * 8)) + (rawIpAddress[3] & 0xFF)) & 0xffffffffL;
531 private void makeConnectedRoute(BigInteger dpId, long vpnId, VrfEntry vrfEntry, String rd,
532 List<ActionInfo> actionInfos, int addOrRemove) {
533 LOG.trace("makeConnectedRoute: vrfEntry {}",vrfEntry);
534 String values[] = vrfEntry.getDestPrefix().split("/");
535 String ipAddress = values[0];
536 int prefixLength = (values.length == 1) ? 0 : Integer.parseInt(values[1]);
537 LOG.debug("Adding route to DPN. ip {} masklen {}", ipAddress, prefixLength);
538 InetAddress destPrefix = null;
540 destPrefix = InetAddress.getByName(ipAddress);
541 } catch (UnknownHostException e) {
542 LOG.error("UnknowHostException in addRoute. Failed to add Route for ipPrefix {}", vrfEntry.getDestPrefix());
546 List<MatchInfo> matches = new ArrayList<MatchInfo>();
548 matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
549 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
551 matches.add(new MatchInfo(MatchFieldType.eth_type,
552 new long[] { 0x0800L }));
554 if(prefixLength != 0) {
555 matches.add(new MatchInfo(MatchFieldType.ipv4_dst, new long[] {
556 getIpAddress(destPrefix.getAddress()), prefixLength }));
559 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
560 if(addOrRemove == NwConstants.ADD_FLOW) {
561 instructions.add(new InstructionInfo(InstructionType.write_actions, actionInfos));
564 String flowRef = getFlowRef(dpId, NwConstants.L3_FIB_TABLE, rd, destPrefix);
566 FlowEntity flowEntity;
568 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
569 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_FIB_TABLE, flowRef,
570 priority, flowRef, 0, 0,
571 COOKIE_VM_FIB_TABLE, matches, instructions);
573 if (addOrRemove == NwConstants.ADD_FLOW) {
574 /* We need to call sync API to install flow so that 2 DS operations on the same object do not
575 * happen at same time. However, MDSALManager's syncInstallFlow takes a delay time (or uses a default one) to wait
576 * for or for notification that operational DS write for flows is done. We do not turn on the stats writing for flows,
577 * so that notification never comes, so we do not need that wait. Sending the lowest value of wait "1 ms" since 0 wait means
578 * wait indefinitely. */
579 mdsalManager.syncInstallFlow(flowEntity, 1);
581 mdsalManager.syncRemoveFlow(flowEntity, 1);
585 private void makeLFibTableEntry(BigInteger dpId, long label, long groupId,
586 String nextHop, int addOrRemove) {
587 List<MatchInfo> matches = new ArrayList<MatchInfo>();
588 matches.add(new MatchInfo(MatchFieldType.eth_type,
589 new long[] { 0x8847L }));
590 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(label)}));
592 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
593 List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
594 actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
595 actionsInfos.add(new ActionInfo(ActionType.group, new String[] { String.valueOf(groupId) }));
596 instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos));
598 // Install the flow entry in L3_LFIB_TABLE
599 String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, nextHop);
601 FlowEntity flowEntity;
602 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef,
603 DEFAULT_FIB_FLOW_PRIORITY, flowRef, 0, 0,
604 COOKIE_VM_LFIB_TABLE, matches, instructions);
606 if (addOrRemove == NwConstants.ADD_FLOW) {
607 /* We need to call sync API to install flow so that 2 DS operations on the same object do not
608 * happen at same time. However, MDSALManager's syncInstallFlow takes a delay time (or uses a default one) to wait
609 * for or for notification that operational DS write for flows is done. We do not turn on the stats writing for flows,
610 * so that notification never comes, so we do not need that wait. Sending the lowest value of wait "1 ms" since 0 wait means
611 * wait indefinitely. */
613 mdsalManager.syncInstallFlow(flowEntity, 1);
615 mdsalManager.syncRemoveFlow(flowEntity, 1);
617 LOG.debug("LFIB Entry for dpID {} : label : {} group {} modified successfully {}",dpId, label, groupId );
620 private void deleteLocalAdjacency(final BigInteger dpId, final long vpnId, final String ipAddress) {
621 LOG.trace("deleteLocalAdjacency called with dpid {}, vpnId{}, ipAddress {}",dpId, vpnId, ipAddress);
623 nextHopManager.removeLocalNextHop(dpId, vpnId, ipAddress);
624 } catch (NullPointerException e) {
629 public void populateFibOnNewDpn(BigInteger dpnId, long vpnId, String rd) {
630 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
631 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
632 Optional<VrfTables> vrfTable = FibUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
633 if(vrfTable.isPresent()) {
634 for(VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
635 // Passing null as we don't know the dpn
636 // to which prefix is attached at this point
637 createRemoteFibEntry(null, dpnId, vpnId, vrfTable.get().getKey(), vrfEntry);
642 public void cleanUpDpnForVpn(BigInteger dpnId, long vpnId, String rd) {
643 LOG.trace("Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
644 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
645 Optional<VrfTables> vrfTable = FibUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
646 if(vrfTable.isPresent()) {
647 for(VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
648 // Passing null as we don't know the dpn
649 // to which prefix is attached at this point
650 deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().getKey(), vrfEntry);
655 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
656 InstanceIdentifierBuilder<VrfTables> idBuilder =
657 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
658 InstanceIdentifier<VrfTables> id = idBuilder.build();
662 private String getFlowRef(BigInteger dpnId, short tableId, long label, String nextHop) {
663 return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
664 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
665 .append(label).append(NwConstants.FLOWID_SEPARATOR)
666 .append(nextHop).toString();
669 private String getFlowRef(BigInteger dpnId, short tableId, String rd, InetAddress destPrefix) {
670 return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
671 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
672 .append(rd).append(NwConstants.FLOWID_SEPARATOR)
673 .append(destPrefix.getHostAddress()).toString();
676 protected String resolveAdjacency(final BigInteger localDpnId, final BigInteger remoteDpnId,
677 final long vpnId, final VrfEntry vrfEntry, String rd) {
678 String adjacency = null;
679 boolean staticRoute = false;
680 LOG.trace("resolveAdjacency called with localdpid{} remotedpid {}, vpnId{}, VrfEntry {}", localDpnId, remoteDpnId, vpnId, vrfEntry);
682 Extraroute extra_route = getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
683 if(extra_route != null) {
688 nextHopManager.getRemoteNextHopPointer(localDpnId, remoteDpnId, vpnId,
689 vrfEntry.getDestPrefix(),
690 (staticRoute == true) ? extra_route.getNexthopIp() : vrfEntry.getNextHopAddress());
691 } catch (NullPointerException e) {
697 protected VpnInstanceOpDataEntry getVpnInstance(String rd) {
698 InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.create(VpnInstanceOpData.class).child(
699 VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd));
700 Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
701 if(vpnInstanceOpData.isPresent()) {
702 return vpnInstanceOpData.get();
707 public void processNodeAdd(BigInteger dpnId) {
708 LOG.debug("Received notification to install TableMiss entries for dpn {} ", dpnId);
709 makeTableMissFlow(dpnId, NwConstants.ADD_FLOW);
710 makeL3IntfTblMissFlow(dpnId, NwConstants.ADD_FLOW);
713 private void makeTableMissFlow(BigInteger dpnId, int addOrRemove) {
714 final BigInteger COOKIE_TABLE_MISS = new BigInteger("1030000", 16);
715 // Instruction to goto L3 InterfaceTable
716 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
717 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.L3_INTERFACE_TABLE }));
718 List<MatchInfo> matches = new ArrayList<MatchInfo>();
719 FlowEntity flowEntityLfib = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_LFIB_TABLE,
720 getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, NwConstants.TABLE_MISS_FLOW),
721 NwConstants.TABLE_MISS_PRIORITY, "Table Miss", 0, 0, COOKIE_TABLE_MISS, matches, instructions);
723 FlowEntity flowEntityFib = MDSALUtil.buildFlowEntity(dpnId,NwConstants.L3_FIB_TABLE, getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, NwConstants.TABLE_MISS_FLOW),
724 NwConstants.TABLE_MISS_PRIORITY, "FIB Table Miss Flow", 0, 0, COOKIE_VM_FIB_TABLE,
725 matches, instructions);
727 if (addOrRemove == NwConstants.ADD_FLOW) {
728 LOG.debug("Invoking MDSAL to install Table Miss Entries");
729 mdsalManager.installFlow(flowEntityLfib);
730 mdsalManager.installFlow(flowEntityFib);
732 mdsalManager.removeFlow(flowEntityLfib);
733 mdsalManager.removeFlow(flowEntityFib);
738 private String getFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
739 return new StringBuffer().append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
740 .append(tableId).append(NwConstants.FLOWID_SEPARATOR).append(tableMiss)
741 .append(FLOWID_PREFIX).toString();
745 * Install flow entry in protocol table to forward mpls
746 * coming through gre tunnel to LFIB table.
748 private void makeProtocolTableFlow(BigInteger dpnId, int addOrRemove) {
749 final BigInteger COOKIE_PROTOCOL_TABLE = new BigInteger("1070000", 16);
750 // Instruction to goto L3 InterfaceTable
751 List<InstructionInfo> instructions = new ArrayList<>();
752 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] {NwConstants.L3_LFIB_TABLE}));
753 List<MatchInfo> matches = new ArrayList<MatchInfo>();
754 matches.add(new MatchInfo(MatchFieldType.eth_type,
755 new long[] { 0x8847L }));
756 FlowEntity flowEntityToLfib = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_PROTOCOL_TABLE,
757 getFlowRef(dpnId, NwConstants.L3_PROTOCOL_TABLE,
758 NwConstants.L3_LFIB_TABLE),
759 DEFAULT_FIB_FLOW_PRIORITY,
760 "Protocol Table For LFIB",
762 COOKIE_PROTOCOL_TABLE,
763 matches, instructions);
765 if (addOrRemove == NwConstants.ADD_FLOW) {
766 LOG.debug("Invoking MDSAL to install Protocol Entries for dpn {}", dpnId);
767 mdsalManager.installFlow(flowEntityToLfib);
769 mdsalManager.removeFlow(flowEntityToLfib);
773 public List<String> printFibEntries() {
774 List<String> result = new ArrayList<String>();
775 result.add(String.format(" %-7s %-20s %-20s %-7s", "RD", "Prefix", "Nexthop", "Label"));
776 result.add("-------------------------------------------------------------------");
777 InstanceIdentifier<FibEntries> id = InstanceIdentifier.create(FibEntries.class);
778 Optional<FibEntries> fibEntries = FibUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
779 if (fibEntries.isPresent()) {
780 List<VrfTables> vrfTables = fibEntries.get().getVrfTables();
781 for (VrfTables vrfTable : vrfTables) {
782 for (VrfEntry vrfEntry : vrfTable.getVrfEntry()) {
783 result.add(String.format(" %-7s %-20s %-20s %-7s", vrfTable.getRouteDistinguisher(),
784 vrfEntry.getDestPrefix(), vrfEntry.getNextHopAddress(), vrfEntry.getLabel()));
791 private void makeL3IntfTblMissFlow(BigInteger dpnId, int addOrRemove) {
792 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
793 List<MatchInfo> matches = new ArrayList<MatchInfo>();
794 final BigInteger COOKIE_TABLE_MISS = new BigInteger("1030000", 16);
795 // Instruction to clear metadata except SI and LportTag bits
796 instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
797 CLEAR_METADATA, METADATA_MASK_CLEAR }));
798 // Instruction to clear action
799 instructions.add(new InstructionInfo(InstructionType.clear_actions));
800 // Instruction to goto L3 InterfaceTable
802 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.LPORT_DISPATCHER_TABLE }));
804 FlowEntity flowEntityL3Intf = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_INTERFACE_TABLE,
805 getFlowRef(dpnId, NwConstants.L3_INTERFACE_TABLE, NwConstants.TABLE_MISS_FLOW),
806 NwConstants.TABLE_MISS_PRIORITY, "L3 Interface Table Miss", 0, 0, COOKIE_TABLE_MISS, matches, instructions);
807 if (addOrRemove == NwConstants.ADD_FLOW) {
808 LOG.info("Invoking MDSAL to install L3 interface Table Miss Entries");
809 mdsalManager.installFlow(flowEntityL3Intf);
811 mdsalManager.removeFlow(flowEntityL3Intf);