2 * Copyright (c) 2015 Dell, Inc. 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
9 package org.opendaylight.ovsdb.openstack.netvirt.sfc.standalone.openflow13;
11 import com.google.common.base.Preconditions;
13 import com.google.common.collect.Iterables;
14 import java.util.List;
15 import java.util.StringTokenizer;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
21 import org.opendaylight.ovsdb.openstack.netvirt.sfc.INetvirtSfcOF13Provider;
22 import org.opendaylight.ovsdb.openstack.netvirt.sfc.ISfcClassifierService;
23 import org.opendaylight.ovsdb.openstack.netvirt.sfc.NshUtils;
24 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
25 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
26 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
27 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePaths;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
34 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathKey;
35 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.Matches;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.acl.rev150105.RedirectToSfc;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.Bridges;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.osgi.framework.ServiceReference;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * Open vSwitch OpenFlow 1.3 Networking Provider for Netvirt SFC
60 public class NetvirtSfcStandaloneOF13Provider implements INetvirtSfcOF13Provider {
61 private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcStandaloneOF13Provider.class);
62 private static final short TABLE_0_CLASSIFIER = 0;
63 private static final short TABLE_3_INGR_ACL = 50;
65 private volatile NodeCacheManager nodeCacheManager;
66 private volatile Southbound southbound;
67 private MdsalUtils mdsalUtils;
68 private SfcClassifier sfcClassifier;
70 // TBD:: Remove these constants after integrating with openstack.
71 private static final String TUNNEL_DST = "192.168.50.75";
72 private static final String TUNNEL_VNID = "10";
73 private static final String CLIENT_PORT_NAME = "vethl-h35_2";
74 private static final String SERVER_PORT_NAME = "vethl-h35_4";
75 private static final String CLIENT_GPE_PORT_NAME = "sw1-vxlangpe-0";
76 private static final String SERVER_GPE_PORT_NAME = "sw6-vxlangpe-0";
77 private static final String INTERFACE_TYPE_VXLAN_GPE = "vxlangpe";
80 * {@link NetvirtSfcStandaloneOF13Provider} constructor.
81 * @param dataBroker MdSal {@link DataBroker}
83 public NetvirtSfcStandaloneOF13Provider(final DataBroker dataBroker) {
84 Preconditions.checkNotNull(dataBroker, "Input dataBroker cannot be NULL!");
85 mdsalUtils = new MdsalUtils(dataBroker);
86 //this.setDependencies(null);
87 sfcClassifier = new SfcClassifier(dataBroker, southbound, mdsalUtils);
90 public void removeClassifierRules(Sff sff, Acl acl) {
91 // TODO Auto-generated method stub
95 public void addClassifierRules(Acl acl) {
96 String aclName = acl.getAclName();
97 Classifiers classifiers = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, getClassifierIid());
98 if (classifiers == null) {
99 LOG.debug("add: No Classifiers found");
103 LOG.debug("add: Classifiers: {}", classifiers);
104 for (Classifier classifier : classifiers.getClassifier()) {
105 if (classifier.getAcl().equals(aclName)) {
106 if (classifier.getBridges() != null) {
107 addClassifierRules(classifier.getBridges(), acl);
114 public void removeClassifierRules(Acl acl) {
115 String aclName = acl.getAclName();
116 Classifiers classifiers = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, getClassifierIid());
117 if (classifiers != null) {
118 for (Classifier classifier : classifiers.getClassifier()) {
119 if (classifier.getAcl().equalsIgnoreCase(aclName)) {
120 if (classifier.getSffs() != null) {
121 for (Sff sff : classifier.getSffs().getSff()) {
122 removeClassifierRules(sff, acl);
131 public void setSfcClassifierService(ISfcClassifierService sfcClassifierService) {
136 public void addClassifierRules(Bridge bridge, Acl acl) {
141 public void addClassifierRules(Bridges bridges, Acl acl) {
142 Preconditions.checkNotNull(bridges, "Input bridges cannot be NULL!");
143 Preconditions.checkNotNull(acl, "Input accesslist cannot be NULL!");
145 for (Ace ace : acl.getAccessListEntries().getAce()) {
146 processAclEntry(ace, bridges, true);
150 private void processAclEntry(Ace entry, Bridges bridges, boolean write) {
151 Matches matches = entry.getMatches();
152 if (matches == null) {
153 LOG.warn("processAclEntry: matches not found");
157 RenderedServicePath rsp = getRenderedServicePath(entry);
159 LOG.warn("Failed to get renderedServicePatch for entry: {}", entry);
163 LOG.info("processAclEntry: RSP: {}", rsp);
164 List<RenderedServicePathHop> pathHopList = rsp.getRenderedServicePathHop();
165 if (pathHopList.isEmpty()) {
166 LOG.warn("Service Path = {} has empty hops!!", rsp.getName());
170 for (Bridge bridge : bridges.getBridge()) {
171 if (bridge.getDirection().getIntValue() == 0) {
172 Node bridgeNode = getBridgeNode(bridge.getName());
173 if (bridgeNode == null) {
174 LOG.debug("processAclEntry: bridge {} not yet configured. Skip processing !!", bridge.getName());
178 long tunnelOfPort = southbound.getOFPort(bridgeNode, CLIENT_GPE_PORT_NAME);
179 if (tunnelOfPort == 0L) {
180 LOG.error("programAclEntry: Could not identify tunnel port {} -> OF ({}) on {}",
181 CLIENT_GPE_PORT_NAME, tunnelOfPort, bridgeNode);
185 long localOfPort = southbound.getOFPort(bridgeNode, CLIENT_PORT_NAME);
186 if (localOfPort == 0L) {
187 LOG.error("programAclEntry: Could not identify local port {} -> OF ({}) on {}",
188 CLIENT_GPE_PORT_NAME, localOfPort, bridgeNode);
192 // Find the first Hop within an RSP.
193 // The classifier flow needs to send all matched traffic to this first hop SFF.
194 RenderedServicePathFirstHop firstRspHop = SfcProviderRenderedPathAPI
195 .readRenderedServicePathFirstHop(new RspName(rsp.getName()));
197 LOG.debug("First Hop IPAddress = {}, Port = {}", firstRspHop.getIp().getIpv4Address().getValue(),
198 firstRspHop.getPort().getValue());
200 NshUtils nshHeader = new NshUtils();
201 // C1 is the normal overlay dest ip and c2 is the vnid
202 // Hardcoded for now, netvirt integration will have those values
203 nshHeader.setNshMetaC1(NshUtils.convertIpAddressToLong(new Ipv4Address(TUNNEL_DST)));
204 nshHeader.setNshMetaC2(Long.parseLong(TUNNEL_VNID));
205 nshHeader.setNshNsp(rsp.getPathId());
207 RenderedServicePathHop firstHop = pathHopList.get(0);
208 nshHeader.setNshNsi(firstHop.getServiceIndex());
209 nshHeader.setNshTunIpDst(firstRspHop.getIp().getIpv4Address());
210 nshHeader.setNshTunUdpPort(firstRspHop.getPort());
211 LOG.debug("The Nsh Header = {}", nshHeader);
213 handleLocalInPort(southbound.getDataPathId(bridgeNode), rsp.getPathId().toString(), localOfPort,
214 TABLE_0_CLASSIFIER, TABLE_3_INGR_ACL, matches, true);
216 handleSfcClassiferFlows(southbound.getDataPathId(bridgeNode), TABLE_3_INGR_ACL, entry.getRuleName(),
217 matches, nshHeader, tunnelOfPort, true);
219 Node bridgeNode = getBridgeNode(bridge.getName());
220 if (bridgeNode == null) {
221 LOG.debug("processAclEntry: bridge {} not yet configured. Skip processing !!", bridge.getName());
225 long tunnelOfPort = southbound.getOFPort(bridgeNode, SERVER_GPE_PORT_NAME);
226 if (tunnelOfPort == 0L) {
227 LOG.error("programAclEntry: Could not identify tunnel port {} -> OF ({}) on {}",
228 CLIENT_GPE_PORT_NAME, tunnelOfPort, bridgeNode);
232 long localOfPort = southbound.getOFPort(bridgeNode, SERVER_PORT_NAME);
233 if (localOfPort == 0L) {
234 LOG.error("programAclEntry: Could not identify local port {} -> OF ({}) on {}",
235 CLIENT_GPE_PORT_NAME, localOfPort, bridgeNode);
239 RenderedServicePathHop lastRspHop = Iterables.getLast(rsp.getRenderedServicePathHop());
241 LOG.debug("programAclEntry: Last Hop #: {}, nsi: {}", lastRspHop.getHopNumber().intValue(),
242 lastRspHop.getServiceIndex().intValue() - 1);
244 NshUtils nshHeader = new NshUtils();
245 nshHeader.setNshNsp(rsp.getPathId());
246 nshHeader.setNshNsi((short)(lastRspHop.getServiceIndex().intValue() - 1));
247 nshHeader.setNshMetaC2(Long.parseLong(TUNNEL_VNID));
248 LOG.debug("programAclEntry: The Nsh Header = {}", nshHeader);
250 //handleLocalEgressPort(southbound.getDataPathId(bridgeNode), rsp.getPathId().toString(), localOfPort,
251 // TABLE_0_CLASSIFIER, TABLE_3_INGR_ACL, true);
253 handleEgressSfcClassiferFlows(southbound.getDataPathId(bridgeNode),
254 TABLE_0_CLASSIFIER, entry.getRuleName(), matches, nshHeader, tunnelOfPort, localOfPort, true);
259 private RenderedServicePath getRenderedServicePath (Ace entry) {
260 RenderedServicePath rsp = null;
261 RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
262 LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
263 if (sfcRedirect == null) {
264 LOG.warn("processAClEntry: sfcRedirect is null");
268 if (sfcRedirect.getRspName() != null) {
269 rsp = getRenderedServicePathFromRsp(sfcRedirect.getRspName());
270 } else if (sfcRedirect.getSfpName() != null) {
271 LOG.warn("getRenderedServicePath: sfp not handled yet");
273 rsp = getRenderedServicePathFromSfc(entry);
275 LOG.info("getRenderedServicePath: rsp: {}", rsp);
279 private RenderedServicePath getRenderedServicePathFromRsp(String rspName) {
280 return null;//getRsp(rspName);
283 private RenderedServicePath getRenderedServicePathFromSfc (Ace entry) {
284 RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
285 LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
286 if (sfcRedirect == null) {
287 LOG.warn("processAClEntry: sfcRedirect is null");
291 String sfcName = sfcRedirect.getSfcName();
292 ServiceFunctionPath sfp = getSfp(sfcName);
293 if (sfp == null || sfp.getName() == null) {
294 LOG.warn("There is no configured SFP with sfcName = {}; so skip installing the ACL entry!!", sfcName);
298 LOG.debug("Processing Redirect to SFC = {}, SFP = {}", sfcName, sfp);
299 // If RSP doesn't exist, create an RSP.
300 String sfpName = sfp.getName().getValue();
301 RenderedServicePath rsp = getRspforSfp(sfpName);
302 String rspName = sfp.getName().getValue() + "_rsp";
304 LOG.info("No configured RSP corresponding to SFP = {}, Creating new RSP = {}", sfpName, rspName);
305 CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder()
306 .setParentServiceFunctionPath(sfpName)
308 .setSymmetric(sfp.isSymmetric())
310 rsp = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, rspInput);
312 LOG.warn("failed to add RSP");
316 // If SFP is symmetric, create RSP in the reverse direction.
317 if (sfp.isSymmetric()) {
318 LOG.info("SFP = {} is symmetric, installing RSP in the reverse direction!!", sfpName);
319 String rspNameRev = rspName + "-Reverse";
320 RenderedServicePath rspReverse = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
321 getRspId(rspNameRev));
322 if (rspReverse == null) {
323 rspReverse = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
324 if (rspReverse == null) {
325 LOG.warn("failed to add reverse RSP");
334 private void handleLocalEgressPort(long dataPathId, String s, long localOfPort, short writeTable,
335 short gotoTable, boolean write) {
339 private void handleEgressSfcClassiferFlows(long dataPathId, short writeTable, String ruleName,
340 Matches matches, NshUtils nshHeader, long tunnelOfPort,
341 long outOfPort, boolean write) {
342 sfcClassifier.programEgressSfcClassiferFlows(dataPathId, writeTable, ruleName, matches, nshHeader,
343 tunnelOfPort, outOfPort, write);
346 private void handleSfcClassiferFlows(long dataPathId, short writeTable, String ruleName,
347 Matches matches, NshUtils nshHeader, long tunnelOfPort,
349 sfcClassifier.programSfcClassiferFlows(dataPathId, writeTable, ruleName, matches, nshHeader,
350 tunnelOfPort, write);
353 private InstanceIdentifier<RenderedServicePaths> getRspsId() {
354 return InstanceIdentifier.builder(RenderedServicePaths.class).build();
357 private InstanceIdentifier<RenderedServicePath> getRspId(String rspName) {
358 return InstanceIdentifier.builder(RenderedServicePaths.class)
359 .child(RenderedServicePath.class, new RenderedServicePathKey(new RspName(rspName))).build();
362 public Node getBridgeNode(String bridgeName) {
363 Node nodeFound = null;
364 final List<Node> nodes = nodeCacheManager.getBridgeNodes();
365 if (nodes != null && !nodes.isEmpty()) {
366 for (Node node : nodes) {
367 if (southbound.getBridge(node, bridgeName) != null) {
376 public RenderedServicePath getRspforSfp(String sfpName) {
377 RenderedServicePath rspFound = null;
378 RenderedServicePaths rsps = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, this.getRspsId());
380 for (RenderedServicePath rsp : rsps.getRenderedServicePath()) {
381 if (rsp.getParentServiceFunctionPath() != null) {
382 if (rsp.getParentServiceFunctionPath().getValue().equals(sfpName)) {
391 public ServiceFunctionPath getSfp(String redirectSfc) {
392 ServiceFunctionPath sfpFound = null;
393 ServiceFunctionPaths sfps = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
395 for (ServiceFunctionPath sfp: sfps.getServiceFunctionPath()) {
396 if (sfp.getServiceChainName().getValue().equalsIgnoreCase(redirectSfc)) {
405 * (TABLE:0) EGRESS VM TRAFFIC TOWARDS TEP
406 * MATCH: DESTINATION ETHERNET ADDR AND OPENFLOW INPORT
407 * INSTRUCTION: SET TUNNELID AND GOTO TABLE TUNNEL TABLE (N)
408 * TABLE=0,IN_PORT=2,DL_SRC=00:00:00:00:00:01 \
409 * ACTIONS=SET_FIELD:5->TUN_ID,GOTO_TABLE=1"
411 public String getDestIp(Matches match) {
412 if (match.getAceType() instanceof AceIp) {
413 AceIp aceIp = (AceIp)match.getAceType();
414 if (aceIp.getAceIpVersion() instanceof AceIpv4) {
415 AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion();
416 if (aceIpv4.getDestinationIpv4Network() != null) {
417 String ipAddrPrefix = aceIpv4.getDestinationIpv4Network().getValue();
418 return new StringTokenizer(ipAddrPrefix, "/").nextToken();
425 public String getSourceIp(Matches match) {
426 if (match.getAceType() instanceof AceIp) {
427 AceIp aceIp = (AceIp)match.getAceType();
428 if (aceIp.getAceIpVersion() instanceof AceIpv4) {
429 AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion();
430 if (aceIpv4.getSourceIpv4Network() != null) {
431 //String ipAddr = new StringTokenizer(ipAddrPrefix, "/").nextToken();
432 return aceIpv4.getSourceIpv4Network().getValue();
439 private InstanceIdentifier<Classifiers> getClassifierIid() {
440 return InstanceIdentifier.create(Classifiers.class);
443 public void handleLocalInPort(long dpidLong, String segmentationId, Long inPort,
444 short writeTable, short goToTableId, Matches matches, boolean write) {
445 sfcClassifier.programLocalInPort(dpidLong, segmentationId, inPort, writeTable, goToTableId, matches, write);
449 public void setDependencies(ServiceReference serviceReference) {
450 nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
451 southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);